Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d5ea5b19e | ||
|
|
fd099570ef | ||
|
|
29693ee954 | ||
|
|
4c37790542 | ||
|
|
5e140339c6 | ||
|
|
d1e615258a | ||
|
|
aa8b0064a1 | ||
|
|
cc70741566 | ||
|
|
d0d0ecfa84 |
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug in REA
|
||||
title: "[Bug] "
|
||||
labels: ["bug", "triage"]
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Bug Description
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
What you expected to happen.
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
What actually happened, including any error messages or unexpected output.
|
||||
|
||||
## Environment
|
||||
|
||||
- REA version (`rea --version` or `npm list @morluto/rea`):
|
||||
- Node.js version (`node --version`):
|
||||
- macOS version:
|
||||
- Hopper Disassembler version:
|
||||
- Target binary format (Mach-O, ELF, PE, .hop):
|
||||
|
||||
## Additional Context
|
||||
|
||||
- MCP client and version (if applicable):
|
||||
- Any relevant logs or stack traces:
|
||||
- Does the issue reproduce with a different target binary?
|
||||
@@ -1,155 +0,0 @@
|
||||
name: Bug report
|
||||
description: Report a reproducible problem in REA
|
||||
title: "[Bug] "
|
||||
labels:
|
||||
- bug
|
||||
- triage
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please report one primary behavior per issue. Keep observed behavior
|
||||
separate from suspected causes, and remove credentials and private paths.
|
||||
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the open and closed issues for a related report.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Area
|
||||
options:
|
||||
- Setup, packaging, or upgrade
|
||||
- Provider or capability discovery
|
||||
- Process capture or runtime observation
|
||||
- Native analysis or decompilation
|
||||
- Evidence, artifacts, or reconstruction
|
||||
- CLI
|
||||
- MCP or agent integration
|
||||
- Documentation
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: observed-behavior
|
||||
attributes:
|
||||
label: Observed behavior
|
||||
description: What happened? Include the command, MCP tool, provider, target type, and conditions that matter.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Give the smallest realistic command, configuration, input, or event sequence that demonstrates the problem.
|
||||
placeholder: |
|
||||
1. Configure or start ...
|
||||
2. Run ...
|
||||
3. Observe ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: What result, contract, invariant, recovery action, or state transition should have occurred?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: impact-scope
|
||||
attributes:
|
||||
label: Impact and scope
|
||||
description: What is affected, what is not affected, and does this affect evidence integrity, process isolation, compatibility, or recovery?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: rea-version
|
||||
attributes:
|
||||
label: REA version
|
||||
description: Use `rea --version` or provide the source revision.
|
||||
placeholder: 3.1.0 or git revision
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: runtime
|
||||
attributes:
|
||||
label: Runtime and platform
|
||||
description: Include Node.js, OS, architecture, and Hopper/Ghidra version when relevant.
|
||||
placeholder: Node 24.18; macOS 15 arm64; Hopper 5.15
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: target
|
||||
attributes:
|
||||
label: Target and artifact context
|
||||
description: Include binary format, app/container type, artifact kind, or workload details when relevant.
|
||||
placeholder: Mach-O arm64 app; Evidence v2 bundle; not applicable
|
||||
|
||||
- type: dropdown
|
||||
id: interface
|
||||
attributes:
|
||||
label: Interface
|
||||
options:
|
||||
- CLI
|
||||
- MCP
|
||||
- Python bridge
|
||||
- Package or setup
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: failure-stage
|
||||
attributes:
|
||||
label: Earliest failure stage
|
||||
description: Select where the incorrect behavior first becomes observable.
|
||||
options:
|
||||
- Request parsing or validation
|
||||
- Setup or provider admission
|
||||
- Capture or observation
|
||||
- Artifact or evidence registration
|
||||
- Native analysis or reconstruction
|
||||
- Transport or result projection
|
||||
- Cleanup, cancellation, or lifecycle
|
||||
- Unknown
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: relevant-evidence
|
||||
attributes:
|
||||
label: Relevant output or evidence
|
||||
description: Paste the smallest useful CLI output, MCP result, error, trace, or evidence excerpt. Redact sensitive values.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
id: typed-outcome
|
||||
attributes:
|
||||
label: Typed outcome or recovery expected
|
||||
description: Optional. What typed result, error, next tool, cleanup state, or limitation should the caller have received?
|
||||
|
||||
- type: textarea
|
||||
id: analysis
|
||||
attributes:
|
||||
label: Analysis or suspected cause
|
||||
description: Optional. Clearly mark source-derived reasoning or a hypothesis; do not present an unverified cause as fact.
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add links, reduced fixtures, screenshots, or a proposed regression test.
|
||||
@@ -1,69 +0,0 @@
|
||||
name: Design discussion
|
||||
description: Resolve an REA architecture, provider, or contract question before implementation
|
||||
title: "[Design] "
|
||||
labels:
|
||||
- enhancement
|
||||
- triage
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Use this for a substantial provider, public/persisted contract, or
|
||||
authority-boundary decision. Keep one decision per discussion.
|
||||
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the open and closed issues and pull requests for a related discussion.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: decision
|
||||
attributes:
|
||||
label: Decision needed
|
||||
description: State the exact question maintainers need to resolve.
|
||||
placeholder: Should REA ... or ...?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: context-evidence
|
||||
attributes:
|
||||
label: Context and evidence
|
||||
description: Explain why the decision is needed now. Separate observed behavior, derived evidence, and inference.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: boundary-invariants
|
||||
attributes:
|
||||
label: Boundary and invariants
|
||||
description: Identify the owning layer, provider/bridge boundary, artifact or evidence invariants, and any process or authorization constraints.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: options
|
||||
attributes:
|
||||
label: Options and tradeoffs
|
||||
description: Compare a small number of viable approaches, including platform, compatibility, safety, evidence, and maintenance costs.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: success-criteria
|
||||
attributes:
|
||||
label: Success criteria and open questions
|
||||
description: State what outcome would settle the decision and list only unresolved questions that could change the choice.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: references
|
||||
attributes:
|
||||
label: References
|
||||
description: Link relevant contracts, upstream provider documentation, tests, measurements, or prior discussions.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Documentation
|
||||
about: Suggest improvements to REA documentation
|
||||
title: "[Docs] "
|
||||
labels: ["documentation", "triage"]
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Documentation Area
|
||||
|
||||
Which documentation needs improvement? (README, AGENTS.md, API docs, inline JSDoc, skill files)
|
||||
|
||||
## Problem
|
||||
|
||||
What is unclear, missing, or incorrect in the current documentation?
|
||||
|
||||
## Suggested Improvement
|
||||
|
||||
Describe the change you would like to see.
|
||||
|
||||
## Additional Context
|
||||
|
||||
- Link to the relevant documentation file or section:
|
||||
- Any examples or references that would help:
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Documentation
|
||||
description: Report unclear, missing, or incorrect REA documentation
|
||||
title: "[Docs] "
|
||||
labels:
|
||||
- documentation
|
||||
- triage
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the existing issues for a related documentation report.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: location
|
||||
attributes:
|
||||
label: Documentation location
|
||||
description: Link the page, section, command, API, skill, or inline comment.
|
||||
placeholder: README.md#quick-start or docs/mcp-contracts.md
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem
|
||||
description: What is unclear, missing, outdated, or incorrect, and who is affected?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-content
|
||||
attributes:
|
||||
label: Expected content
|
||||
description: Describe the accurate behavior or example that should be documented.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: evidence
|
||||
attributes:
|
||||
label: Supporting evidence
|
||||
description: Add source, command output, provider documentation, or a link that supports the correction.
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Include screenshots, affected translations, or related issues.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Propose a new feature or enhancement for REA
|
||||
title: "[Feature] "
|
||||
labels: ["enhancement", "triage"]
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
What problem does this feature solve? Describe the use case and why existing tools are insufficient.
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
Describe the feature or enhancement you would like to see. Include:
|
||||
|
||||
- Which REA tool(s) this affects (direct, enhanced, or session tools)
|
||||
- Expected inputs and outputs
|
||||
- How it integrates with the existing MCP workflow
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
Any alternative approaches or workarounds you have tried.
|
||||
|
||||
## Additional Context
|
||||
|
||||
- Relevant Hopper API methods or concepts:
|
||||
- Links to related issues or discussions:
|
||||
- Would this require changes to the bridge (`bridge/hopper_bridge.py`)?
|
||||
@@ -1,88 +0,0 @@
|
||||
name: Feature request
|
||||
description: Propose a focused improvement to REA
|
||||
title: "[Feature] "
|
||||
labels:
|
||||
- enhancement
|
||||
- triage
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Keep one primary capability or workflow outcome per request. For a
|
||||
substantial provider, public contract, or architecture change, explain
|
||||
the decision maintainers need to make before implementation.
|
||||
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the open and closed issues and pull requests for a related proposal.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Area
|
||||
options:
|
||||
- Setup, packaging, or upgrade
|
||||
- Provider or capability discovery
|
||||
- Process capture or runtime observation
|
||||
- Native analysis or decompilation
|
||||
- Evidence, artifacts, or reconstruction
|
||||
- CLI
|
||||
- MCP or agent integration
|
||||
- Documentation
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current-limitation
|
||||
attributes:
|
||||
label: Current limitation
|
||||
description: What user or agent workflow is blocked, ambiguous, or unnecessarily difficult today?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: use-cases
|
||||
attributes:
|
||||
label: Use cases
|
||||
description: Give concrete applications, targets, callers, or investigation workflows. Explain why current tools are insufficient.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: desired-outcome
|
||||
attributes:
|
||||
label: Desired outcome and success criteria
|
||||
description: Describe observable behavior, inputs, outputs, evidence, and compatibility criteria that would make this useful.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: boundaries
|
||||
attributes:
|
||||
label: Authority boundaries and non-goals
|
||||
description: Call out process execution, target mutation, provider licensing, evidence provenance, supported platforms, and work that should remain out of scope.
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
description: Describe existing REA operations, upstream tools, or workarounds considered and why they do not meet the need.
|
||||
|
||||
- type: textarea
|
||||
id: possible-direction
|
||||
attributes:
|
||||
label: Possible direction
|
||||
description: Optional implementation or integration idea. Treat it as a proposal, not a requirement.
|
||||
|
||||
- type: textarea
|
||||
id: references
|
||||
attributes:
|
||||
label: References
|
||||
description: Link relevant provider documentation, source, measurements, artifacts, issues, or discussions.
|
||||
@@ -1,77 +1,32 @@
|
||||
<!--
|
||||
PR title: type(optional-scope): imperative outcome
|
||||
The title becomes the squash-merge commit subject and generated changelog entry.
|
||||
-->
|
||||
## Description
|
||||
|
||||
## Summary
|
||||
<!-- What does this change do? Why is it needed? -->
|
||||
|
||||
<!-- What user, agent, or maintainer problem does this solve? Link the issue
|
||||
with "Fixes #123" when applicable. Keep the prior behavior, expected contract,
|
||||
and new behavior understandable without private context. -->
|
||||
## Type of Change
|
||||
|
||||
## Problem and expected behavior
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature
|
||||
- [ ] Documentation update
|
||||
- [ ] Performance improvement
|
||||
- [ ] Refactoring (no functional change)
|
||||
- [ ] Tool contract change (MCP schema modification)
|
||||
|
||||
<!-- For fixes, give the smallest trigger and violated invariant. For features,
|
||||
describe the use case and observable outcome. -->
|
||||
## Testing Performed
|
||||
|
||||
## Change and scope
|
||||
<!-- Describe the testing you have done to verify this change. -->
|
||||
|
||||
<!-- Explain the chosen approach, why it fits REA's architecture, and what is
|
||||
intentionally not included. -->
|
||||
- [ ] Unit tests added or updated
|
||||
- [ ] TypeScript typecheck passes (`npm run typecheck`)
|
||||
- [ ] Linting passes (`npm run lint`)
|
||||
- [ ] Full test suite passes (`npm test`)
|
||||
- [ ] Real Hopper verification performed (`npm run verify:hopper`)
|
||||
- [ ] Package verification performed (`npm run verify:package`)
|
||||
|
||||
## Contract and boundary impact
|
||||
## Tool Contract Impact
|
||||
|
||||
<!-- Complete the applicable lines; use "none" or "not applicable" explicitly. -->
|
||||
<!-- If this PR adds, removes, or modifies MCP tools, list them here. -->
|
||||
<!-- The current inventory is 42 tools (31 direct + 8 enhanced + 3 session). -->
|
||||
|
||||
- Semantic owner and earliest changed stage:
|
||||
- CLI and MCP/tool-catalog contract:
|
||||
- Provider, bridge, target-format, or platform compatibility:
|
||||
- Evidence, artifact, provenance, or reconstruction contract:
|
||||
- Process execution, authorization, cleanup, or containment impact:
|
||||
- Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `skill-src/` and never commit build outputs):
|
||||
## Related Issues
|
||||
|
||||
## Evidence and regression coverage
|
||||
|
||||
<!-- State whether evidence is executed, source-derived, or proposed. For fixes,
|
||||
explain how the regression would fail on the affected base revision when practical. -->
|
||||
|
||||
- Tests added or updated:
|
||||
- Base reproduction or other evidence:
|
||||
- User-visible CLI/MCP output (if applicable):
|
||||
- Remaining proof gaps:
|
||||
|
||||
For evidence-bearing changes:
|
||||
|
||||
- [ ] Observed, derived, and inferred claims remain distinguishable.
|
||||
- [ ] Artifact identity, source provenance, and failed attempts remain preserved.
|
||||
- [ ] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.
|
||||
|
||||
## Validation performed
|
||||
|
||||
<!-- List only commands that actually ran, with observed results. Include the
|
||||
relevant real-provider or platform workflow, or explain why it was unavailable. -->
|
||||
|
||||
- `command` — result
|
||||
|
||||
## Compatibility, safety, and release
|
||||
|
||||
<!-- Call out breaking changes, migrations, package effects, platform/provider
|
||||
limits, security/privacy/containment implications, and meaningful proof gaps. -->
|
||||
|
||||
- Breaking changes or migration steps:
|
||||
<!-- For ! / BREAKING CHANGE: show a previously valid call/configuration,
|
||||
its new behavior and migration, and why compatibility/deprecation cannot
|
||||
preserve it. Compatible fixes/additions should use patch/minor semantics.
|
||||
See docs/releasing.md. -->
|
||||
- Real Hopper/Ghidra, browser, or OS coverage:
|
||||
- Package or release metadata impact:
|
||||
- Security, privacy, process, or containment review:
|
||||
|
||||
## Review checklist
|
||||
|
||||
- [ ] The PR has one focused outcome and the title follows `type(scope): outcome`.
|
||||
- [ ] Related issue is linked, or the reason for not linking one is stated above.
|
||||
- [ ] Tests cover changed observable behavior and meaningful failure paths.
|
||||
- [ ] Owning docs, contracts, and generated metadata are updated where needed.
|
||||
- [ ] User-visible CLI/MCP changes include representative output.
|
||||
- [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims.
|
||||
<!-- Link issues this PR closes or relates to. Use "Closes #123" syntax. -->
|
||||
|
||||
+68
-457
@@ -4,121 +4,11 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main, "release/*"]
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
pr-title:
|
||||
name: Conventional PR title
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate title for Release Please
|
||||
env:
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
run: >-
|
||||
node -e 'const title = process.env.PR_TITLE ?? "";
|
||||
const conventional = /^(?:feat|fix|docs|refactor|perf|test|build|ci|chore|revert)(?:\([a-z0-9._/-]+\))?!?: .+/u;
|
||||
if (!conventional.test(title)) {
|
||||
console.error("PR title must use Conventional Commits, for example: feat: add historical source import");
|
||||
process.exit(1);
|
||||
}'
|
||||
|
||||
changes:
|
||||
name: Classify changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
code: ${{ steps.package.outputs.required }}
|
||||
package: ${{ steps.package.outputs.required }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Select package and Windows lanes
|
||||
id: package
|
||||
shell: bash
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
|
||||
echo "required=true" >> "${GITHUB_OUTPUT}"
|
||||
elif git diff --quiet "${BASE_SHA}" "${HEAD_SHA}" -- \
|
||||
. \
|
||||
':(exclude)docs/**' \
|
||||
':(exclude)AGENTS.md' \
|
||||
':(exclude)CONTRIBUTING.md'; then
|
||||
echo "required=false" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo "required=true" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
|
||||
static:
|
||||
name: Static checks
|
||||
needs: changes
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Require successful change classification
|
||||
env:
|
||||
CHANGES_RESULT: ${{ needs.changes.result }}
|
||||
run: test "${CHANGES_RESULT}" = "success"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
run: npm run check:ci
|
||||
- if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
run: npm run verify:test-discovery
|
||||
- if: github.event_name == 'pull_request' && needs.changes.outputs.code != 'true'
|
||||
run: npm run format:check
|
||||
|
||||
test-shard:
|
||||
name: Test shard (${{ matrix.shard }})
|
||||
needs: [changes, build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1/4, 2/4, 3/4, 4/4]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rea-dist
|
||||
path: dist
|
||||
- name: Generate projections from downloaded build
|
||||
run: npm run build:projections
|
||||
- run: npm run test:ci:shard:run -- --shard=${{ matrix.shard }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: vitest-report-${{ strategy.job-index }}
|
||||
path: .vitest/blob/*
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
overwrite: true
|
||||
retention-days: 7
|
||||
|
||||
build:
|
||||
name: Build package
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
typecheck:
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -127,186 +17,71 @@ jobs:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run build:cached
|
||||
- uses: actions/upload-artifact@v4
|
||||
- run: npm run typecheck
|
||||
|
||||
lint:
|
||||
name: Lint (oxlint)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
name: rea-dist
|
||||
path: dist
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
|
||||
format:
|
||||
name: Format Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run format:check
|
||||
|
||||
dead-code:
|
||||
name: Dead Code (knip)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run lint:dead
|
||||
|
||||
duplicates:
|
||||
name: Duplicate Code (jscpd)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run jscpd
|
||||
|
||||
tech-debt:
|
||||
name: Tech Debt Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run scan:todos
|
||||
|
||||
test:
|
||||
name: Test (coverage + timing)
|
||||
needs: [changes, test-shard]
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Require successful test shards
|
||||
env:
|
||||
CHANGES_RESULT: ${{ needs.changes.result }}
|
||||
CODE_REQUIRED: ${{ needs.changes.outputs.code }}
|
||||
SHARD_RESULT: ${{ needs.test-shard.result }}
|
||||
run: |
|
||||
test "${CHANGES_RESULT}" = "success"
|
||||
test "${CODE_REQUIRED}" != "true" || test "${SHARD_RESULT}" = "success"
|
||||
- uses: actions/checkout@v4
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
- uses: actions/setup-node@v4
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- if: needs.changes.outputs.code == 'true' && success()
|
||||
run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
with:
|
||||
path: .vitest/blob
|
||||
pattern: vitest-report-*
|
||||
merge-multiple: true
|
||||
- name: Merge coverage, JUnit, and timing reports
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
run: >-
|
||||
npm run test:ci:merge --
|
||||
--coverage.reportsDirectory=coverage
|
||||
--coverage.reporter=text
|
||||
--coverage.reporter=json-summary
|
||||
--coverage.reporter=lcov
|
||||
--reporter=default
|
||||
--reporter=junit
|
||||
--reporter=json
|
||||
--outputFile.junit=test-results.xml
|
||||
--outputFile.json=test-results.json
|
||||
- name: Summarize slowest test files
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
shell: bash
|
||||
run: |
|
||||
node <<'NODE' >> "${GITHUB_STEP_SUMMARY}"
|
||||
const report = JSON.parse(require("node:fs").readFileSync("test-results.json", "utf8"));
|
||||
const files = (report.testResults ?? [])
|
||||
.map((result) => ({
|
||||
name: result.name,
|
||||
duration: Math.max(0, (result.endTime ?? result.startTime) - result.startTime),
|
||||
}))
|
||||
.sort((left, right) => right.duration - left.duration)
|
||||
.slice(0, 20);
|
||||
console.log("## Slowest deterministic test files\n");
|
||||
console.log("| File | Duration (ms) |");
|
||||
console.log("| --- | ---: |");
|
||||
for (const file of files) {
|
||||
console.log(`| \`${file.name}\` | ${file.duration} |`);
|
||||
}
|
||||
NODE
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: needs.changes.outputs.code == 'true' && always()
|
||||
with:
|
||||
name: test-results
|
||||
path: |
|
||||
test-results.xml
|
||||
test-results.json
|
||||
coverage/
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
|
||||
conformance-fixtures:
|
||||
name: Source-built conformance fixtures
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Verify fixtures from a clean checkout
|
||||
run: npm run verify:fixtures
|
||||
|
||||
windows-native-build:
|
||||
name: Build bundled Windows x64 native controls
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Prepare cross compiler
|
||||
run: |
|
||||
# Slow Azure archive transfers can exhaust the job before compilation.
|
||||
# Keep the same signed packages and avoid slow HTTP mirror fallback.
|
||||
for source in /etc/apt/apt-mirrors.txt /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do
|
||||
if [ -f "$source" ]; then
|
||||
sudo sed -i 's|azure.archive.ubuntu.com|archive.ubuntu.com|g; s|http://archive.ubuntu.com|https://archive.ubuntu.com|g' "$source"
|
||||
fi
|
||||
done
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
- run: npm run build:windows-native
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
if-no-files-found: error
|
||||
|
||||
package-e2e:
|
||||
name: Native package baseline (${{ matrix.platform }} ${{ matrix.architecture }})
|
||||
needs: [changes, windows-native-build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: ubuntu-24.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: macos-15
|
||||
platform: darwin
|
||||
architecture: arm64
|
||||
- os: macos-15-intel
|
||||
platform: darwin
|
||||
architecture: x64
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=1024"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }}
|
||||
run: >-
|
||||
node -e "const assert = require('node:assert/strict');
|
||||
assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM);
|
||||
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
|
||||
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- run: npm run verify:package
|
||||
- name: Verify real Node Inspector discovery and observation
|
||||
if: matrix.platform == 'darwin'
|
||||
run: npm run verify:inspector
|
||||
|
||||
apple-artifacts:
|
||||
name: Real Apple artifact CLI/MCP E2E
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
@@ -314,171 +89,17 @@ jobs:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run verify:keyed-archive
|
||||
- run: npm run verify:asset-catalog
|
||||
- run: npm run verify:macos-bundle
|
||||
- name: Native installer and compiled NIB hierarchy regressions
|
||||
run: npm run test:focused -- tests/boundary/filesystem/installScript.test.ts tests/boundary/filesystem/nibNativeHierarchy.test.ts tests/boundary/filesystem/interfaceBuilderBundle.test.ts
|
||||
- name: Real binary metadata and Interface Builder integration
|
||||
run: npm run verify:apple-dispatch && npm run verify:interface-builder
|
||||
- name: Native plist CLI selection regressions
|
||||
run: npm run test:focused -- tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts tests/boundary/cli/nativePlistSelectionFailureCli.test.ts
|
||||
- name: Native Swift demangling CLI and MCP regressions
|
||||
run: npm run test:focused -- tests/boundary/cli/nativeDemangleCli.test.ts tests/boundary/mcp/nativeDemangleMcp.test.ts
|
||||
- name: Process filesystem snapshot descriptor regressions
|
||||
run: npm run test:focused -- src/process/capture/FilesystemSnapshot.test.ts
|
||||
- name: Native Darwin ownership and process cleanup regressions
|
||||
run: npm run test:focused -- tests/boundary/process/darwinProcessRunTokenReader.test.ts tests/boundary/process/processCaptureLifecycle.test.ts tests/boundary/cli/processCli.test.ts tests/boundary/mcp/sessionMcp.test.ts
|
||||
|
||||
reconstruction-readiness:
|
||||
name: Reconstruction readiness
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Run the public CLI/MCP reconstruction journey
|
||||
env:
|
||||
REA_READINESS_REPORT_PATH: readiness-report.json
|
||||
run: |
|
||||
set -o pipefail
|
||||
npm run verify:readiness | tee readiness-summary.json
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: reconstruction-readiness
|
||||
path: |
|
||||
readiness-summary.json
|
||||
readiness-report.json
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
|
||||
javascript-runtime-observation:
|
||||
name: JavaScript runtime observation (${{ matrix.os }})
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: ubuntu-24.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: windows-latest
|
||||
platform: win32
|
||||
architecture: x64
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=1024"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }}
|
||||
run: >-
|
||||
node -e "const assert = require('node:assert/strict');
|
||||
assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM);
|
||||
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
|
||||
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
|
||||
- run: npm ci
|
||||
- name: Verify real Node Inspector discovery and observation
|
||||
run: npm run verify:inspector
|
||||
|
||||
windows-curated:
|
||||
name: Windows capability and package lane
|
||||
needs: [changes, windows-native-build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run build:cached
|
||||
- run: npm run typecheck
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- name: Record named Windows host capabilities
|
||||
run: node scripts/windows-capabilities.mjs | Out-File -Encoding utf8NoBOM windows-capabilities.json
|
||||
- name: Validate Windows host capability report
|
||||
run: >-
|
||||
node -e "const report = JSON.parse(require('node:fs').readFileSync('windows-capabilities.json', 'utf8'));
|
||||
if (report.platform !== 'win32' || report.architecture !== 'x64')
|
||||
throw new Error('expected a Windows x64 capability report');"
|
||||
# Keep this list aligned with tests/process-global/curatedWindowsLane.test.ts,
|
||||
# which fails when any path below is renamed or removed. Vitest silently
|
||||
# ignores unmatched paths, so a stale entry would otherwise report green
|
||||
# while skipping coverage.
|
||||
#
|
||||
# The matching native artifact establishes real Job Object and private
|
||||
# runtime boundaries for these fixtures. Ghidra and Java are not needed;
|
||||
# real engine claims remain in the separate controlled-provider workflow.
|
||||
- name: Run curated Windows capability tests
|
||||
run: >-
|
||||
npx vitest run --coverage.enabled=false
|
||||
tests/boundary/filesystem/binaryTarget.test.ts
|
||||
tests/boundary/filesystem/safeOutputTree.test.ts
|
||||
tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts
|
||||
tests/boundary/filesystem/setupUnavailableProvider.test.ts
|
||||
tests/boundary/filesystem/windowsNpxRegistration.test.ts
|
||||
src/application/ClientRegistrationIdentity.test.ts
|
||||
tests/boundary/filesystem/webScriptManifestPaths.test.ts
|
||||
tests/acceptance/webModuleCli.test.ts
|
||||
tests/acceptance/webSourceLocationCli.test.ts
|
||||
tests/boundary/cli/referenceSourceImportStatus.test.ts
|
||||
src/cliReferenceSourceImportStatus.test.ts
|
||||
tests/boundary/filesystem/asarInventoryFreshness.test.ts
|
||||
tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
|
||||
tests/conformance/providers/ghidra/windowsCapabilities.test.ts
|
||||
src/ghidra/GhidraInstallation.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTransport.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraLauncher.test.ts
|
||||
src/windows/WindowsNativeLoader.test.ts
|
||||
src/windows/WindowsOwnedProcess.test.ts
|
||||
src/ghidra/GhidraLauncherEnvironment.test.ts
|
||||
src/process/ProcessOwnership.test.ts
|
||||
src/process/ProcessOwnership.part2.test.ts
|
||||
src/process/ProcessOwnership.part3.test.ts
|
||||
src/process/ProcessOwnership.validation.test.ts
|
||||
src/application/Update.test.ts
|
||||
tests/process-global/updateMaintenance.test.ts
|
||||
tests/conformance/providers/ghidra/realGhidraWindowsWorkflow.test.ts
|
||||
- run: npm run verify:windows-native
|
||||
- run: npm run verify:package:windows
|
||||
- run: npm run rebuild:native
|
||||
- run: npm run test:coverage -- --reporter=verbose --reporter=junit --outputFile.junit=test-results.xml
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: windows-capabilities
|
||||
path: windows-capabilities.json
|
||||
name: test-results
|
||||
path: test-results.xml
|
||||
retention-days: 30
|
||||
|
||||
docs:
|
||||
name: Docs Generation
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -487,14 +108,4 @@ jobs:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:check
|
||||
- name: Generation leaves authored files unchanged
|
||||
run: git diff --exit-code
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: generated-docs
|
||||
path: |
|
||||
docs/public/product-catalog.json
|
||||
docs/verification/managed-conformance-*.json
|
||||
skills/reverse-engineer-anything
|
||||
if-no-files-found: error
|
||||
- run: npm run docs:generate
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
name: Source-built NativeAOT fixtures
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "bridge/ghidra/extensions/nativeaot/**"
|
||||
- "scripts/generate-nativeaot-fixtures.mjs"
|
||||
- "tests/fixtures/nativeaot/**"
|
||||
- ".github/workflows/nativeaot-fixtures.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
windows-fixture:
|
||||
name: Build Windows x64 NativeAOT fixture
|
||||
# .NET 8.0.22's bundled linker discovery supports VS 2019/2022.
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
|
||||
DOTNET_PROCESSOR_COUNT: "1"
|
||||
COMPlus_GCHeapHardLimit: "30000000"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "8.0.416"
|
||||
- name: Build benign fixture and independent byte oracle
|
||||
run: node scripts/generate-nativeaot-fixtures.mjs
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nativeaot-win-x64-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: |
|
||||
_reference/nativeaot-integration/generated/win-x64/symbols/NativeAotFixture.exe
|
||||
_reference/nativeaot-integration/generated/win-x64/NativeAotFixture.map
|
||||
_reference/nativeaot-integration/generated/win-x64/symbols.txt
|
||||
_reference/nativeaot-integration/generated/win-x64/oracle.json
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Documentation build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: documentation-build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:build
|
||||
@@ -1,78 +0,0 @@
|
||||
name: Real offline binary layout
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
large_mcp:
|
||||
description: "Also verify the complete 65,281-section result over MCP (slow)"
|
||||
type: boolean
|
||||
default: false
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-binary-layout.yml"
|
||||
- "bridge/pwntools/**"
|
||||
- "src/native/pwntools/**"
|
||||
- "src/domain/native/binaryLayout*"
|
||||
- "src/application/binaryDiagnostics/**"
|
||||
- "src/contracts/native/binaryDiagnosticsToolContracts.ts"
|
||||
- "src/composition/binaryDiagnostics.ts"
|
||||
- "src/server/registerBinaryDiagnosticsTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/binaryDiagnosticsCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/process/OwnedCommand.ts"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProviderDeadline.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "src/domain/analysisError*.ts"
|
||||
- "scripts/verify-binary-layout.mjs"
|
||||
- "scripts/lib/elf-section-name-fixtures.mjs"
|
||||
- "scripts/lib/large-result-mcp.mjs"
|
||||
- "scripts/fixtures/binary-layout*"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-binary-layout-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
REA_VERIFY_LARGE_ELF_MCP: ${{ inputs.large_mcp && '1' || '0' }}
|
||||
|
||||
jobs:
|
||||
real-binary-layout:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Acquire exact upstream libraries in an isolated runner environment
|
||||
shell: bash
|
||||
run: |
|
||||
layout_python_root="${RUNNER_TEMP}/rea-pwntools-layout"
|
||||
python3 -m venv "${layout_python_root}"
|
||||
"${layout_python_root}/bin/python" -m pip install --disable-pip-version-check \
|
||||
'pwntools==4.15.0' 'pyelftools==0.33' 'unicorn==2.1.2'
|
||||
command -v gcc
|
||||
command -v ld
|
||||
command -v strip
|
||||
command -v strace
|
||||
echo "REA_PWNTOOLS_PYTHON=${layout_python_root}/bin/python" >> "${GITHUB_ENV}"
|
||||
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
||||
- name: Verify source-owned fixtures through public CLI and MCP
|
||||
run: npm run verify:binary:layout
|
||||
@@ -1,64 +0,0 @@
|
||||
name: Real browser verification
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-browser.yml"
|
||||
- "scripts/verify-real-browser.mjs"
|
||||
- "scripts/verify-browser-network.mjs"
|
||||
- "scripts/verify-browser-scripts.mjs"
|
||||
- "scripts/verify-browser-modules.mjs"
|
||||
- "scripts/verify-browser-dom-destinations.mjs"
|
||||
- "scripts/lib/browser-*.mjs"
|
||||
- "scripts/fixtures/browser-network-site.mjs"
|
||||
- "scripts/fixtures/browser-script-site.mjs"
|
||||
- "scripts/fixtures/browser-module-site.mjs"
|
||||
- "scripts/fixtures/browser-verifier-site.mjs"
|
||||
- "scripts/fixtures/page-cdp-proxy.mjs"
|
||||
- "src/browser/**"
|
||||
- "src/domain/browser*.ts"
|
||||
- "src/domain/webScript*.ts"
|
||||
- "src/domain/webModule*.ts"
|
||||
- "src/application/WebModule*.ts"
|
||||
- "src/composition/webModules.ts"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "src/artifacts/ArtifactPaths.ts"
|
||||
- "src/application/WebScriptExportService.ts"
|
||||
- "src/contracts/webScriptToolContracts.ts"
|
||||
- "src/server/registerWebScriptTool.ts"
|
||||
- "src/server/registerWebModuleTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/webModuleCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/cli/webScriptCommands.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: real-browser-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
real-browser:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run verify:browser
|
||||
env:
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
@@ -1,66 +0,0 @@
|
||||
name: Real offline EVM interface
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-evm-interface.yml"
|
||||
- "src/evm/**"
|
||||
- "src/domain/evm/**"
|
||||
- "src/application/evm/**"
|
||||
- "src/contracts/evm/**"
|
||||
- "src/composition/evm.ts"
|
||||
- "src/cli/evmCommands.ts"
|
||||
- "src/server/registerEvmTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/process/OwnedCommand.ts"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "src/process/ProviderDeadline.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "scripts/verify-evm-interface.mjs"
|
||||
- "scripts/fixtures/evm-interface.sol"
|
||||
- "third_party/evmole/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-evm-interface-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
|
||||
jobs:
|
||||
real-evm-interface:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Acquire unchanged fixture compiler in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
evm_fixture_root="${RUNNER_TEMP}/rea-evm-fixture-compiler"
|
||||
mkdir -p "${evm_fixture_root}"
|
||||
npm install --prefix "${evm_fixture_root}" --ignore-scripts --no-audit --no-fund --save-exact solc@0.8.30
|
||||
command -v prlimit
|
||||
command -v strace
|
||||
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
||||
echo "REA_VERIFY_SOLC_MODULE=${evm_fixture_root}/node_modules/solc/index.js" >> "${GITHUB_ENV}"
|
||||
- name: Verify source-owned compiled bytecode through public CLI and MCP
|
||||
run: npm run verify:evm:interface
|
||||
@@ -1,84 +0,0 @@
|
||||
name: Real Ghidra Windows E2E
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [real-ghidra-windows]
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
native-artifact:
|
||||
name: Build matching Windows native artifact
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Prepare cross compiler
|
||||
run: |
|
||||
for source in /etc/apt/apt-mirrors.txt /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do
|
||||
if [ -f "$source" ]; then
|
||||
sudo sed -i 's|azure.archive.ubuntu.com|archive.ubuntu.com|g; s|http://archive.ubuntu.com|https://archive.ubuntu.com|g' "$source"
|
||||
fi
|
||||
done
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
- run: npm run build:windows-native
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: real-ghidra-windows-native
|
||||
path: native/windows/build/
|
||||
if-no-files-found: error
|
||||
|
||||
verify:
|
||||
name: Real Ghidra 12.1.4 Windows x64 PE proof
|
||||
if: github.ref == 'refs/heads/main'
|
||||
needs: native-artifact
|
||||
environment: real-ghidra-windows
|
||||
runs-on: [self-hosted, Windows, x64, ghidra-12-1-2]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: real-ghidra-windows-native
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- run: npm run verify:windows-native
|
||||
- name: Verify all admitted Windows Ghidra operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows | Tee-Object -FilePath windows-ghidra-proof.log
|
||||
- name: Verify native x86 Windows Ghidra operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows -- --x86 | Tee-Object -FilePath windows-ghidra-x86-proof.log
|
||||
- name: Verify ordinary-user CLI and MCP operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows:package | Tee-Object -FilePath windows-ghidra-package-proof.log
|
||||
- name: Verify ordinary-user x86 CLI and MCP operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows:package -- --x86 | Tee-Object -FilePath windows-ghidra-x86-package-proof.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: windows-ghidra-proof
|
||||
path: |
|
||||
windows-ghidra-proof.log
|
||||
windows-ghidra-package-proof.log
|
||||
windows-ghidra-x86-proof.log
|
||||
windows-ghidra-x86-package-proof.log
|
||||
retention-days: 30
|
||||
@@ -1,56 +0,0 @@
|
||||
name: Real Hopper Linux E2E
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
clean-setup:
|
||||
name: Clean setup, CLI, and MCP
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Pack CLI for setup verification
|
||||
shell: bash
|
||||
run: |
|
||||
tarball="$(npm pack --silent)"
|
||||
prefix="${RUNNER_TEMP}/rea-prefix"
|
||||
npm install --global --prefix "${prefix}" "./${tarball}"
|
||||
echo "REA_VERIFY_SERVER_COMMAND=${prefix}/bin/rea" >> "${GITHUB_ENV}"
|
||||
- name: Verify clean Linux setup through CLI and MCP
|
||||
run: sudo --preserve-env=REA_VERIFY_SERVER_COMMAND,PATH npm run verify:setup:linux
|
||||
|
||||
verify:
|
||||
name: Installed package against real Linux Hopper
|
||||
runs-on: [self-hosted, Linux, hopper]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install packed CLI in isolation
|
||||
shell: bash
|
||||
run: |
|
||||
tarball="$(npm pack --silent)"
|
||||
prefix="${RUNNER_TEMP}/rea-prefix"
|
||||
npm install --global --ignore-scripts --prefix "${prefix}" "./${tarball}"
|
||||
echo "REA_VERIFY_SERVER_COMMAND=${prefix}/bin/rea" >> "${GITHUB_ENV}"
|
||||
echo 'REA_VERIFY_SERVER_ARGS_JSON=["mcp"]' >> "${GITHUB_ENV}"
|
||||
- name: Verify source-owned ELF fixtures through an alternate launcher symlink
|
||||
env:
|
||||
REA_HOPPER_CONFORMANCE_MANIFEST_PATH: ${{ github.workspace }}/build/conformance/manifest.json
|
||||
run: npm run verify:hopper:linux
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Real Hopper E2E
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
name: Installed package against real Hopper
|
||||
runs-on: [self-hosted, macOS, hopper]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install packed CLI in isolation
|
||||
shell: bash
|
||||
run: |
|
||||
tarball="$(npm pack --silent)"
|
||||
prefix="${RUNNER_TEMP}/rea-prefix"
|
||||
npm install --global --ignore-scripts --prefix "${prefix}" "./${tarball}"
|
||||
echo "REA_VERIFY_SERVER_COMMAND=${prefix}/bin/rea" >> "${GITHUB_ENV}"
|
||||
echo 'REA_VERIFY_SERVER_ARGS_JSON=["mcp"]' >> "${GITHUB_ENV}"
|
||||
- name: Verify source-owned fixtures with real Hopper
|
||||
env:
|
||||
REA_HOPPER_CONFORMANCE_MANIFEST_PATH: ${{ github.workspace }}/build/conformance/manifest.json
|
||||
run: npm run verify:hopper
|
||||
@@ -1,68 +0,0 @@
|
||||
name: Real JavaScript recovery
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-javascript-recovery.yml"
|
||||
- "src/javascript/recovery/**"
|
||||
- "src/domain/javascript/**"
|
||||
- "src/application/javascript/JavaScriptRecovery*.ts"
|
||||
- "src/composition/javascriptRecovery.ts"
|
||||
- "src/contracts/javascript/javascriptRecoveryToolContracts.ts"
|
||||
- "src/cli/javascriptRecoveryCommands.ts"
|
||||
- "src/cli/javascriptApplicationAnalysis.ts"
|
||||
- "src/server/registerJavaScriptRecoveryTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/application/CapabilityInventory.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "scripts/rea.mjs"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/SafeOutputTree.ts"
|
||||
- "scripts/verify/javascript/recovery.mjs"
|
||||
- "scripts/fixtures/javascript-recovery/**"
|
||||
- "third_party/wakaru"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: javascript-recovery-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
NODE_OPTIONS: "--max-old-space-size=768"
|
||||
RAYON_NUM_THREADS: "1"
|
||||
|
||||
jobs:
|
||||
real-javascript-recovery:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Fetch pinned Wakaru release into the job workspace
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/rea-wakaru"
|
||||
curl --fail --location --retry 3 https://github.com/pionxzh/wakaru/releases/download/v1.13.0/wakaru-linux-x64.tar.gz -o "$RUNNER_TEMP/rea-wakaru/release.tar.gz"
|
||||
tar -xzf "$RUNNER_TEMP/rea-wakaru/release.tar.gz" -C "$RUNNER_TEMP/rea-wakaru" wakaru
|
||||
printf '%s %s\n' e8c7ca052974604197389f6ace7464e4dd24dce64e2730971521bd6606ec9c9e "$RUNNER_TEMP/rea-wakaru/wakaru" | sha256sum --check
|
||||
- name: Install isolated fixture compilers
|
||||
run: npm install --prefix "$RUNNER_TEMP/rea-fixture-compilers" --ignore-scripts --no-audit --no-fund esbuild@0.25.10 webpack@5.101.3
|
||||
- run: npm run build:cached
|
||||
- run: npm run verify:javascript:recovery
|
||||
env:
|
||||
REA_WAKARU_COMMAND: ${{ runner.temp }}/rea-wakaru/wakaru
|
||||
REA_JAVASCRIPT_FIXTURE_TOOLS: ${{ runner.temp }}/rea-fixture-compilers
|
||||
@@ -1,99 +0,0 @@
|
||||
name: Real recorded crash evidence
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-recorded-crash.yml"
|
||||
- "bridge/pwntools/**"
|
||||
- "bridge/pwndbg/**"
|
||||
- "src/native/pwntools/**"
|
||||
- "src/native/pwndbg/**"
|
||||
- "src/domain/native/recordedCrash*"
|
||||
- "src/application/binaryDiagnostics/**"
|
||||
- "src/contracts/native/binaryDiagnosticsToolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/contracts/toolOutputSchemaGroups.ts"
|
||||
- "src/composition/binaryDiagnostics.ts"
|
||||
- "src/server/registerRecordedCrashTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/cli/binaryDiagnosticsCommands.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/domain/analysisError*.ts"
|
||||
- "scripts/verify-recorded-crash.mjs"
|
||||
- "scripts/verify/recorded-crash/**"
|
||||
- "scripts/lib/recorded-core-fixtures.py"
|
||||
- "scripts/fixtures/recorded-crash.c"
|
||||
- "third_party/pwndbg/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-recorded-crash-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
UV_CONCURRENT_BUILDS: "1"
|
||||
UV_CONCURRENT_INSTALLS: "1"
|
||||
UV_PYTHON_DOWNLOADS: never
|
||||
|
||||
jobs:
|
||||
real-recorded-crash:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install GDB on the disposable CI runner
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install --yes --no-install-recommends gdb
|
||||
- name: Acquire exact unchanged upstreams in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
command -v gcc
|
||||
command -v gdb
|
||||
command -v strace
|
||||
core_decoder_root="${RUNNER_TEMP}/rea-core-decoder"
|
||||
core_pwndbg_root="${RUNNER_TEMP}/rea-core-pwndbg"
|
||||
core_pwndbg_venv="${RUNNER_TEMP}/rea-core-pwndbg-venv"
|
||||
python3 -m venv "${core_decoder_root}"
|
||||
"${core_decoder_root}/bin/python" -m pip install --disable-pip-version-check \
|
||||
'pwntools==4.15.0' 'pyelftools==0.33' 'unicorn==2.1.2' 'uv==0.12.0'
|
||||
git init "${core_pwndbg_root}"
|
||||
git -C "${core_pwndbg_root}" remote add origin https://github.com/pwndbg/pwndbg.git
|
||||
git -C "${core_pwndbg_root}" fetch --depth=1 origin 55596aa676a2756efb8d67034f31675170eabe48
|
||||
git -C "${core_pwndbg_root}" checkout --detach FETCH_HEAD
|
||||
test "$(git -C "${core_pwndbg_root}" rev-parse HEAD)" = 55596aa676a2756efb8d67034f31675170eabe48
|
||||
UV_PROJECT_ENVIRONMENT="${core_pwndbg_venv}" \
|
||||
"${core_decoder_root}/bin/uv" sync --project "${core_pwndbg_root}" \
|
||||
--frozen --no-editable --no-default-groups --python /usr/bin/python3 --no-managed-python
|
||||
test -z "$(git -C "${core_pwndbg_root}" status --porcelain --untracked-files=no)"
|
||||
echo "REA_PWNTOOLS_PYTHON=${core_decoder_root}/bin/python" >> "${GITHUB_ENV}"
|
||||
echo "REA_PWNDBG_GDB=$(command -v gdb)" >> "${GITHUB_ENV}"
|
||||
echo "REA_PWNDBG_GDBINIT=${core_pwndbg_root}/gdbinit.py" >> "${GITHUB_ENV}"
|
||||
echo "REA_PWNDBG_VENV_PATH=${core_pwndbg_venv}" >> "${GITHUB_ENV}"
|
||||
echo "REA_VERIFY_STRACE_COMMAND=$(command -v strace)" >> "${GITHUB_ENV}"
|
||||
- name: Build and install the exact package in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
npm run build:cached
|
||||
npm pack --ignore-scripts --json --pack-destination "${RUNNER_TEMP}" > "${RUNNER_TEMP}/rea-core-pack.json"
|
||||
core_package_name=$(node -p 'JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8"))[0].filename' "${RUNNER_TEMP}/rea-core-pack.json")
|
||||
npm install --prefix "${RUNNER_TEMP}/rea-core-installed" --ignore-scripts --no-fund --no-audit "${RUNNER_TEMP}/${core_package_name}"
|
||||
- name: Verify installed CLI and MCP against the source-owned recording
|
||||
shell: bash
|
||||
run: node scripts/verify-recorded-crash.mjs "${RUNNER_TEMP}/rea-core-installed/node_modules/rea-agents/scripts/rea.mjs"
|
||||
@@ -1,75 +0,0 @@
|
||||
name: Real historical web network captures
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-network-captures.yml"
|
||||
- "bridge/mitmproxy/**"
|
||||
- "src/browser/history/**"
|
||||
- "src/domain/webNetworkCapture*.ts"
|
||||
- "src/domain/analysisError*.ts"
|
||||
- "src/contracts/errorSchemas.ts"
|
||||
- "src/domain/explicitSensitive*.ts"
|
||||
- "src/application/WebNetworkCapture*"
|
||||
- "src/composition/webNetworkCaptures.ts"
|
||||
- "src/cli/webNetworkCaptureCommands.ts"
|
||||
- "src/server/registerWebNetworkCaptureTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/toolRegistrationOptions.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/process/OwnedCommand.ts"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProviderDeadline.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "src/process/capture/ProcessCaptureLifecycle.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "src/contracts/webNetworkCaptureToolContracts.ts"
|
||||
- "scripts/verify-web-network-captures.mjs"
|
||||
- "scripts/fixtures/generate-mitmproxy-capture.py"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: real-web-network-captures-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
|
||||
jobs:
|
||||
real-web-network-captures:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Use pinned package manager
|
||||
run: npm install --global npm@11.16.0
|
||||
- run: npm ci
|
||||
- name: Acquire fixed upstream native profile in runner temporary storage
|
||||
shell: bash
|
||||
run: |
|
||||
capture_tool_root="${RUNNER_TEMP}/rea-mitmproxy-12.2.3"
|
||||
mkdir -p "${capture_tool_root}"
|
||||
curl --fail --location --retry 3 --max-time 180 \
|
||||
https://downloads.mitmproxy.org/12.2.3/mitmproxy-12.2.3-linux-x86_64.tar.gz \
|
||||
--output "${capture_tool_root}/mitmproxy.tar.gz"
|
||||
echo '2e95286b618fa6fd33e5e62a78c2e5112571d85f42ec2bac29b97ee242bdb5c5 mitmproxy.tar.gz' \
|
||||
| (cd "${capture_tool_root}" && sha256sum --check --strict)
|
||||
tar -xzf "${capture_tool_root}/mitmproxy.tar.gz" -C "${capture_tool_root}"
|
||||
echo "REA_MITMDUMP_COMMAND=${capture_tool_root}/mitmdump" >> "${GITHUB_ENV}"
|
||||
- name: Verify upstream producer fixtures through public CLI and MCP
|
||||
run: npm run verify:web:network-captures
|
||||
@@ -1,54 +0,0 @@
|
||||
name: Real website runtime attribution
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-runtime.yml"
|
||||
- "scripts/verify-browser-runtime.mjs"
|
||||
- "scripts/lib/browser-runtime-*.mjs"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "scripts/fixtures/browser-runtime-site.mjs"
|
||||
- "src/browser/execution/**"
|
||||
- "src/browser/CdpConnection.ts"
|
||||
- "src/browser/CdpEndpoint.ts"
|
||||
- "src/browser/CdpTarget*.ts"
|
||||
- "src/browser/CdpCaptureValues.ts"
|
||||
- "src/browser/CdpAuthorized*.ts"
|
||||
- "src/domain/webRuntime.ts"
|
||||
- "src/domain/webExecution.ts"
|
||||
- "src/domain/webEventListeners.ts"
|
||||
- "src/domain/browserObservationError*.ts"
|
||||
- "src/application/WebRuntime*.ts"
|
||||
- "src/composition/webRuntime.ts"
|
||||
- "src/contracts/webRuntimeToolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/server/registerWebRuntimeTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/webRuntimeCommands.ts"
|
||||
- "src/cli/commandCancellation.ts"
|
||||
- "src/cli.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: real-web-runtime-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
HUSKY: "0"
|
||||
jobs:
|
||||
real-web-runtime:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run verify:browser:runtime
|
||||
env:
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
@@ -1,50 +0,0 @@
|
||||
name: Real website source-map verification
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-source-map.yml"
|
||||
- "scripts/verify-browser-source-maps.mjs"
|
||||
- "scripts/lib/browser-source-map-e2e.mjs"
|
||||
- "scripts/fixtures/browser-source-map-site.mjs"
|
||||
- "src/javascript/sourceMaps/**"
|
||||
- "src/browser/assets/**"
|
||||
- "src/domain/webSource*.ts"
|
||||
- "src/domain/webScript*.ts"
|
||||
- "src/application/WebSourceLocation*.ts"
|
||||
- "src/application/ValidateSelectedWebScript.ts"
|
||||
- "src/composition/webSourceLocations.ts"
|
||||
- "src/server/registerWebSourceLocationTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/contracts/webScriptToolContracts.ts"
|
||||
- "src/process/**"
|
||||
- "src/cli/webSourceLocationCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: real-web-source-map-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
HUSKY: "0"
|
||||
jobs:
|
||||
real-web-source-map:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install the pinned compiler into an isolated fixture directory
|
||||
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false esbuild@0.25.10
|
||||
- run: npm run verify:browser:source-maps
|
||||
env:
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
REA_WEB_SOURCE_MAP_COMPILER: ${{ runner.temp }}/rea-web-source-map-compiler/node_modules/esbuild/lib/main.js
|
||||
+12
-251
@@ -1,192 +1,34 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_branch:
|
||||
description: Frozen release branch (release/VERSION)
|
||||
required: true
|
||||
type: string
|
||||
phase:
|
||||
description: Prepare the release PR, or publish its reviewed merge
|
||||
required: true
|
||||
default: prepare
|
||||
type: choice
|
||||
options: [prepare, publish]
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
concurrency:
|
||||
group: release-${{ inputs.release_branch }}
|
||||
cancel-in-progress: false
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
name: Release Please
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
sha: ${{ steps.release.outputs.sha }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
steps:
|
||||
- name: Validate release selection
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
RELEASE_PHASE: ${{ inputs.phase }}
|
||||
run: |
|
||||
if [[ "${RELEASE_PHASE}" != prepare && "${RELEASE_PHASE}" != publish ]]; then
|
||||
echo "phase must be prepare or publish." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_PHASE}" == prepare && "${GITHUB_REF}" != "refs/heads/main" ]]; then
|
||||
echo "Run prepare from main; release_branch selects the frozen source." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE_PHASE}" == publish && "${GITHUB_REF}" != "refs/heads/${RELEASE_BRANCH}" ]]; then
|
||||
echo "Run publish from the selected frozen release branch so npm provenance records its source." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${RELEASE_BRANCH}" =~ ^release/[^/]+$ ]]; then
|
||||
echo "release_branch must select a release/ branch; main is not a release checkpoint." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! git check-ref-format "refs/heads/${RELEASE_BRANCH}"; then
|
||||
echo "Invalid release_branch: ${RELEASE_BRANCH}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Resolve release branch tip
|
||||
id: branch
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
run: |
|
||||
sha="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/heads/${RELEASE_BRANCH}" --jq '.object | select(.type == "commit") | .sha')"
|
||||
if [[ ! "${sha}" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "The release branch tip is not a commit." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "sha=${sha}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Require the publish dispatch to match the branch tip
|
||||
if: inputs.phase == 'publish'
|
||||
shell: bash
|
||||
env:
|
||||
BRANCH_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: |
|
||||
if [[ "${BRANCH_SHA}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The release branch tip differs from the dispatched checkpoint; refusing to create a release for a moved source." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check out release controller and Git history
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js for checkpoint validation
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install checkpoint validation dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Validate checkpoint version and ancestry before release creation
|
||||
env:
|
||||
RELEASE_PHASE: ${{ inputs.phase }}
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: >-
|
||||
node scripts/verify-release-checkpoint.mjs
|
||||
--phase "$RELEASE_PHASE" --release-branch "$RELEASE_BRANCH"
|
||||
--source-sha "$SOURCE_SHA"
|
||||
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
# A dedicated credential starts PR CI automatically; the built-in
|
||||
# token creates approval-required PR runs for maintainers to approve.
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: ${{ inputs.release_branch }}
|
||||
skip-github-release: ${{ inputs.phase == 'prepare' }}
|
||||
skip-github-pull-request: ${{ inputs.phase == 'publish' }}
|
||||
|
||||
- name: Require a merged release PR for publication
|
||||
if: inputs.phase == 'publish' && steps.release.outputs.release_created != 'true'
|
||||
run: |
|
||||
echo "No new release was created. Merge the reviewed release PR into its frozen branch before publishing. For a partial publication, re-run failed jobs in the original run." >&2
|
||||
exit 1
|
||||
|
||||
- name: Bind publication to the dispatched checkpoint
|
||||
if: inputs.phase == 'publish' && steps.release.outputs.release_created == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
if [[ "${RELEASE_SHA}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The tagged release SHA differs from the dispatched checkpoint; refusing to publish mismatched source provenance. The GitHub release already exists; do not dispatch publish again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check out release pull request
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js for generated documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm ci
|
||||
|
||||
- name: Validate prepared release version and migration notes
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
env:
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: >-
|
||||
node scripts/verify-release-checkpoint.mjs
|
||||
--phase prepare --stage candidate --release-branch "$RELEASE_BRANCH"
|
||||
--source-sha "$SOURCE_SHA"
|
||||
|
||||
- name: Regenerate release documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm run docs:generate
|
||||
|
||||
- name: Validate generated release documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
run: npm run docs:check && git diff --exit-code
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish:
|
||||
name: Publish to npm
|
||||
needs: release-please
|
||||
if: inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true'
|
||||
if: needs.release-please.outputs.release_created == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@@ -196,89 +38,8 @@ jobs:
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- name: Prepare Windows native build toolchain
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
|
||||
- name: Build and verify bundled Windows native controls
|
||||
run: npm run build:windows-native && npm run verify:windows-native:artifact
|
||||
|
||||
- name: Verify packaged release before publishing
|
||||
run: npm run verify:package
|
||||
- name: Verify build
|
||||
run: npm run build
|
||||
|
||||
- name: Publish
|
||||
shell: bash
|
||||
run: |
|
||||
version="$(node --input-type=module -e 'import packageJson from "./package.json" with { type: "json" }; process.stdout.write(packageJson.version)')"
|
||||
published_version="$(npm view "rea-agents@${version}" version --json 2>/dev/null || true)"
|
||||
if [ "${published_version}" = "\"${version}\"" ]; then
|
||||
echo "rea-agents@${version} is already published; verifying the existing package."
|
||||
else
|
||||
npm publish --access public
|
||||
fi
|
||||
|
||||
- name: Verify published CLI from npm
|
||||
shell: bash
|
||||
run: |
|
||||
version="$(node --input-type=module -e 'import packageJson from "./package.json" with { type: "json" }; process.stdout.write(packageJson.version)')"
|
||||
verification_root="$(mktemp -d)"
|
||||
trap 'rm -rf -- "${verification_root}"' EXIT
|
||||
cd "${verification_root}"
|
||||
max_attempts=120
|
||||
sleep_seconds=15
|
||||
total_window_minutes=$((max_attempts * sleep_seconds / 60))
|
||||
for attempt in $(seq 1 "${max_attempts}"); do
|
||||
published_version="$(npm view --prefer-online "rea-agents@${version}" version --json 2>/dev/null || true)"
|
||||
if [ "${published_version}" = "\"${version}\"" ]; then
|
||||
echo "rea-agents@${version} is visible on npm after attempt ${attempt}/${max_attempts}; verifying the published CLI."
|
||||
npm exec --yes --package="rea-agents@${version}" -- rea --help
|
||||
npm exec --yes --package="rea-agents@${version}" -- rea --llms
|
||||
node "${GITHUB_WORKSPACE}/scripts/verify-published-package.mjs" "${version}"
|
||||
exit 0
|
||||
else
|
||||
echo "rea-agents@${version} is not visible on npm yet (attempt ${attempt}/${max_attempts}; up to ${total_window_minutes} minutes)" >&2
|
||||
fi
|
||||
if [ "${attempt}" -lt "${max_attempts}" ]; then
|
||||
sleep "${sleep_seconds}"
|
||||
fi
|
||||
done
|
||||
echo "rea-agents@${version} did not become visible on npm within ${total_window_minutes} minutes (${max_attempts} attempts × ${sleep_seconds}s)" >&2
|
||||
echo "This indicates npm registry propagation delay, not a CLI verification failure." >&2
|
||||
exit 1
|
||||
|
||||
publish-mcp:
|
||||
name: Publish to MCP Registry
|
||||
needs: [release-please, publish]
|
||||
if: inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true' && needs.publish.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Verify package and registry metadata versions
|
||||
run: npm run mcp-registry:check
|
||||
|
||||
- name: Install MCP Registry publisher
|
||||
env:
|
||||
MCP_PUBLISHER_VERSION: v1.8.0
|
||||
MCP_PUBLISHER_SHA256_LINUX_AMD64: 1370446bbe74d562608e8005a6ccce02d146a661fbd78674e11cc70b9618d6cf
|
||||
run: |
|
||||
archive="mcp-publisher_linux_amd64.tar.gz"
|
||||
curl -fsSLo "${archive}" \
|
||||
"https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}/${archive}"
|
||||
echo "${MCP_PUBLISHER_SHA256_LINUX_AMD64} ${archive}" | sha256sum --check --strict
|
||||
tar xzf "${archive}" mcp-publisher
|
||||
chmod +x mcp-publisher
|
||||
|
||||
- name: Validate MCP Registry metadata
|
||||
run: ./mcp-publisher validate
|
||||
|
||||
- name: Authenticate to MCP Registry
|
||||
run: ./mcp-publisher login github-oidc
|
||||
|
||||
- name: Publish MCP Registry metadata
|
||||
run: ./mcp-publisher publish
|
||||
run: npm publish --access public
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
name: Website checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, "release/*"]
|
||||
paths:
|
||||
- "website/**"
|
||||
- "scripts/verify-website.py"
|
||||
- "scripts/prepare-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: Prepare the example download
|
||||
run: python3 scripts/prepare-website.py
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
@@ -1,48 +0,0 @@
|
||||
name: Publish REA website
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
pages: read
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare the example download
|
||||
run: python3 scripts/prepare-website.py
|
||||
- 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
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: website/public
|
||||
|
||||
deploy:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Publish to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
+1
-23
@@ -6,7 +6,7 @@ __pycache__/
|
||||
# Distribution
|
||||
dist/
|
||||
build/
|
||||
node_modules
|
||||
node_modules/
|
||||
*.egg-info/
|
||||
*.egg
|
||||
|
||||
@@ -33,29 +33,7 @@ codedb.snapshot
|
||||
htmlcov/
|
||||
.coverage
|
||||
coverage/
|
||||
.vitest-reports/
|
||||
test-results.xml
|
||||
.turbo/
|
||||
.cache/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Local reference sources and analysis artifacts
|
||||
/_reference/
|
||||
|
||||
# Generated website download
|
||||
/website/public/examples/notes-example.zip
|
||||
|
||||
# Build-generated MCP tool catalog (regenerated by `npm run build`; never commit)
|
||||
src/generatedMcpToolCatalog.ts
|
||||
|
||||
# Derived catalogs, portable conformance projections, and packaged skill bundles
|
||||
# (regenerated from this checkout by `npm run build:cached`; never commit)
|
||||
/skills/
|
||||
/docs/public/product-catalog.json
|
||||
/docs/verification/managed-conformance-manifest.json
|
||||
/docs/verification/managed-conformance-ledger.json
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
[submodule "third_party/jadx-headless-mcp"]
|
||||
path = third_party/jadx-headless-mcp
|
||||
url = https://github.com/1013503897/jadx-headless-mcp.git
|
||||
[submodule "third_party/binwalk"]
|
||||
path = third_party/binwalk
|
||||
url = https://github.com/ReFirmLabs/binwalk.git
|
||||
[submodule "third_party/unblob"]
|
||||
path = third_party/unblob
|
||||
url = https://github.com/onekey-sec/unblob.git
|
||||
[submodule "third_party/ghidra-nativeaot"]
|
||||
path = third_party/ghidra-nativeaot
|
||||
url = https://github.com/Washi1337/ghidra-nativeaot.git
|
||||
[submodule "third_party/wakaru"]
|
||||
path = third_party/wakaru
|
||||
url = https://github.com/pionxzh/wakaru.git
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
npx --no-install lint-staged
|
||||
npx lint-staged
|
||||
npm run typecheck
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
npm run check:fast
|
||||
+3
-8
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"_comment": "Test files are intentionally NOT excluded: the gate used to report zero clones because it skipped **/*.test.ts, hiding all test-tree duplication. Current actual is ~2.4% lines across src+ and tests/, down from 3.4% when tests were included. Lower this as duplication is removed.",
|
||||
"threshold": 2.5,
|
||||
"threshold": 3,
|
||||
"reporters": ["consoleFull"],
|
||||
"ignore": [
|
||||
"node_modules/**",
|
||||
@@ -8,12 +7,8 @@
|
||||
"docs/**",
|
||||
".codex/**",
|
||||
".devin/**",
|
||||
"tests/fixtures/**",
|
||||
"third_party/**",
|
||||
"tests/conformance/**/*.c",
|
||||
"tests/conformance/**/*.m",
|
||||
"tests/conformance/**/*.swift",
|
||||
"tests/conformance/**/*.xib"
|
||||
"**/*.test.ts",
|
||||
"tests/fixtures/**"
|
||||
],
|
||||
"gitignore": true,
|
||||
"minLines": 5,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"printWidth": 80,
|
||||
"sortImports": false,
|
||||
"sortPackageJson": false,
|
||||
"ignorePatterns": [
|
||||
"dist/**",
|
||||
"build/**",
|
||||
"docs/api/**",
|
||||
"node_modules/**",
|
||||
"CHANGELOG.md",
|
||||
"package-lock.json",
|
||||
"_reference/**",
|
||||
"third_party/**"
|
||||
]
|
||||
}
|
||||
+6
-85
@@ -6,19 +6,11 @@
|
||||
"max-depth": ["warn", 4],
|
||||
"max-lines": [
|
||||
"warn",
|
||||
{
|
||||
"max": 500,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
{ "max": 500, "skipBlankLines": true, "skipComments": true }
|
||||
],
|
||||
"max-lines-per-function": [
|
||||
"warn",
|
||||
{
|
||||
"max": 100,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
{ "max": 100, "skipBlankLines": true, "skipComments": true }
|
||||
],
|
||||
"max-params": ["warn", 4],
|
||||
"no-var": "warn",
|
||||
@@ -26,69 +18,10 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"scripts/electron-active-hook*.cjs",
|
||||
"tests/conformance/readiness/electron/*.js",
|
||||
"website/public/examples/notes-electron/*.js"
|
||||
],
|
||||
"files": ["tests/**/*.ts"],
|
||||
"rules": {
|
||||
"no-var-requires": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["tests/**/*.ts", "src/**/*.test.ts", "src/**/*.fixture.ts"],
|
||||
"rules": {
|
||||
"max-lines": "off",
|
||||
"max-lines-per-function": [
|
||||
"error",
|
||||
{
|
||||
"max": 150,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/application/**/*.test.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"group": ["**/server/**", "**/cli.*", "**/main.*"],
|
||||
"message": "Service tests must use application ports rather than CLI or MCP entrypoints."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["tests/composition/**/*.test.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"group": [
|
||||
"**/server/**",
|
||||
"**/cli.*",
|
||||
"**/main.*",
|
||||
"node:child_process",
|
||||
"node:net",
|
||||
"node:http",
|
||||
"node:https",
|
||||
"ws",
|
||||
"playwright"
|
||||
],
|
||||
"message": "Composition tests may wire provider-neutral sessions but must not invoke CLI, MCP, process, network, or browser boundaries."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"max-lines-per-function": "off",
|
||||
"max-lines": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -96,17 +29,5 @@
|
||||
"node": true,
|
||||
"es2023": true
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"dist",
|
||||
"build",
|
||||
"node_modules",
|
||||
"docs/api",
|
||||
"coverage",
|
||||
".turbo",
|
||||
".cache",
|
||||
".codex",
|
||||
".devin",
|
||||
".factory",
|
||||
"third_party"
|
||||
]
|
||||
"ignorePatterns": ["dist", "node_modules", "docs/api", ".codex", ".devin"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
dist
|
||||
docs/api
|
||||
node_modules
|
||||
CHANGELOG.md
|
||||
package-lock.json
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "6.0.0"
|
||||
".": "0.3.0"
|
||||
}
|
||||
|
||||
@@ -1,116 +1,61 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Product Direction
|
||||
|
||||
REA exposes reverse-engineering tools through a CLI and MCP server. Hopper, the bring-your-own Ghidra adapter, and the bring-your-own IDA MCP adapter are operation-capable deep binary-analysis providers. Ghidra is supported on Linux x64 and macOS x64/arm64 with matching native decompiler tools, and has an experimental Windows x64 P0 boundary for approved native x86 and x86-64 PE applications; on Linux and macOS it supplies inventory, function analysis, and atomic function annotation edits in an ephemeral database, without modifying executable bytes or controlling a GUI. Windows P0 has no mutation authority. IDA adapts upstream legacy attached GUI and modern headless database-supervisor profiles for live read-only analysis; initial real verification covers Windows, and an attached GUI database is never saved or closed. Keep provider-specific code out of the domain and application layers.
|
||||
|
||||
Prioritize:
|
||||
|
||||
- tool results that distinguish observations, inferences, and unknowns;
|
||||
- equivalent behavior through the CLI and MCP;
|
||||
- additive, idempotent configuration with backups;
|
||||
- end-to-end tests for packaged artifacts and real Hopper/Ghidra claims.
|
||||
|
||||
Installers must not install or upgrade Homebrew, Node.js, npm, Java, Ghidra, or other unrelated software. Ghidra is bring-your-own. `rea setup` must print its planned changes and require approval before writing files or installing Hopper.
|
||||
|
||||
REA is a local-only tool. Preserve caller-selected inputs, captured output, URLs, paths, digests, mismatch locations, and analysis metadata. Do not guess that local evidence is secret from environment-variable names, argument names, or text patterns. Redact transport authentication credentials and values the caller explicitly marks sensitive; do not persist the entire ambient environment merely because a child inherits it.
|
||||
|
||||
## Project Structure & Module Organization
|
||||
|
||||
REA is a layered ESM TypeScript application. Dependencies flow inward from pure domain logic through contracts, providers, application workflows, and CLI/MCP adapters. See [docs/architecture.mermaid](docs/architecture.mermaid) for the component map.
|
||||
REA is a layered ESM TypeScript application. Dependencies flow inward: `domain` ← `contracts` ← `hopper` ← `application` ← `server` ← adapters.
|
||||
|
||||
- `src/domain/` owns pure provider-neutral semantics; `src/contracts/` owns caller-visible schemas and the canonical tool inventory.
|
||||
- `src/hopper/`, `src/ghidra/`, `src/ida/`, `src/browser/`, `src/inspector/`, `src/native/`, `src/artifacts/`, and `src/dotnet/` own provider-specific boundaries. Keep provider protocols out of domain and application code.
|
||||
- `src/application/` composes shared CLI/MCP workflows, including Evidence provenance, unknowns, and eligible snapshot bindings; `src/server/` translates MCP requests; `src/cli.ts` and `src/main.ts` are the CLI and MCP entry points.
|
||||
- `src/process/` owns shared process lifecycle primitives, not provider wire protocols. Reuse its supervision and identity primitives before adding provider-local lifecycle code; a PID and executable pathname alone do not establish ownership after exit or reuse. `bridge/` contains provider-side adapters.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` and capability directories under `scripts/verify/` contain real-toolchain checks.
|
||||
- `docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored build outputs. Update source contracts and authored instructions in `skill-src/`, then run `npm run build:cached`; never commit derived catalog digests or portable conformance projections.
|
||||
- `src/generatedMcpToolCatalog.ts` is build-generated and gitignored. Never commit it; resolve any trace of it in merges by deleting it and running `npm run build:cached`.
|
||||
See [docs/architecture.mermaid](docs/architecture.mermaid) for a visual architecture diagram.
|
||||
|
||||
- `scripts/rea.mjs`: executable dispatcher. Routes only bare `mcp` and `--mcp` to the production stdio server; Incur handles registration utilities and one-shot commands.
|
||||
- `src/main.ts`: MCP adapter. Parses config, wires the shared session runtime, starts stdio transport, and owns process-lifetime shutdown.
|
||||
- `src/cli.ts`: one-shot CLI adapter for setup, diagnostics, analysis, and decompilation.
|
||||
- `src/config.ts`: Zod-validated parsing of environment configuration into `AppConfig`.
|
||||
- `src/domain/`: pure, side-effect-free modules. `errors.ts` (tagged error algebra), `result.ts` (`Result`/`ok`/`err`), `hopperValues.ts` (boundary parsers for Hopper JSON), `symbolAnalysis.ts` (Swift/ObjC name parsing).
|
||||
- `src/contracts/`: caller-visible schemas for 33 direct, 10 enhanced, 5 native, 2 artifact, and 18 session tools; `enhancedInputs.ts` owns enhanced input parsing.
|
||||
- `src/hopper/`: Hopper launch and Unix-socket protocol mechanics. `BridgeLauncher.ts` spawns the Hopper app with the in-process bridge, `HopperClient.ts` correlates request/response over the socket with timeouts and cancellation, `protocol.ts` frames bridge messages.
|
||||
- `bridge/hopper_bridge.py`: runs inside Hopper and adapts declared operations to Hopper's public Python API. Hopper's bundled MCP server is not used.
|
||||
- `src/application/`: shared CLI/MCP session composition, setup and diagnostics, and the 8 enhanced workflows.
|
||||
- `src/server/`: MCP request translation. `createServer.ts` assembles the MCP server, `registerOfficialTools.ts`/`registerEnhancedTools.ts` register each tool set, `toolResult.ts` maps `Result` values to MCP content.
|
||||
- `tests/`: Vitest suite. `tests/fixtures/` holds the fake launcher and fake Hopper bridge used as production seams.
|
||||
- `scripts/verify-real-hopper.mjs`: real-Hopper end-to-end verifier.
|
||||
- `scripts/print-mcp-config.mjs`: prints an MCP server config with absolute paths filled in (`npm run config:print -- /path/to/binary`).
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
- `npm ci`: install the locked dependencies.
|
||||
- `npm run build:cached`: build the CLI and MCP server.
|
||||
- `npm run test:local`: run changed source tests without building; pass exact source test paths to run them regardless of Git status.
|
||||
- `npm run test:focused -- PATH...`: run exact test files; build first for boundary, acceptance, or process-global tests.
|
||||
- `npm run check:changed`: run cached static checks and source tests affected since the branch merge base (default `origin/main`).
|
||||
- `npm run check:fast`: run cached typecheck and lint checks.
|
||||
- `npm run check:pr`: opt into the complete local deterministic gate and generated-document checks for broad changes; CI owns full coverage. Routine iterations need focused tests and relevant checks, not the whole gate each time.
|
||||
- `npm run docs:check`: build and validate generated documents for the current checkout; `npm run docs:generate` regenerates them. CI retains ignored outputs as artifacts and does not push snapshot commits to feature branches.
|
||||
- For provider-dependent changes, see [docs/testing.md](docs/testing.md) and run the matching real-provider verification.
|
||||
- Keep each verification lane's prerequisites limited to the claim it checks. Use host-native fixtures for host/provider acceptance; put optional cross-target formats and their external toolchains in a separate lane. Preflight required commands and report the missing dependency and lane clearly.
|
||||
- `npm ci`: install exact lockfile dependencies.
|
||||
- `npm run build`: compile `src/` into `dist/`.
|
||||
- `npm test`: build, then run the Vitest suite once.
|
||||
- `npm run typecheck`: run strict TypeScript checks without emitting files.
|
||||
- `npm run lint`: apply oxlint rules (complexity, max-lines, unused vars, and TypeScript-specific checks).
|
||||
- `npm run lint:fix`: auto-fix oxlint violations where possible.
|
||||
- `npm run format:check`: verify Prettier formatting.
|
||||
- `npm run check`: run typecheck, lint, format:check, and tests.
|
||||
- `npm run knip`: detect unused files, dependencies, and exports.
|
||||
- `npm run jscpd`: detect duplicate code blocks.
|
||||
- `npm run scan:todos`: scan for TODO, FIXME, and HACK markers.
|
||||
- `npm run verify:hopper`: build and run the real-Hopper verifier with two distinct binaries.
|
||||
- `npm run verify:package`: pack and test the CLI, setup transaction, skill, and 68-tool target-free MCP server in an isolated environment.
|
||||
- `npm run docs:generate`: generate API documentation from JSDoc comments into `docs/api/` using TypeDoc.
|
||||
- `npm run config:print -- /path/to/binary`: print an MCP server config with absolute paths.
|
||||
- `HOPPER_TARGET_PATH=/path/to/binary npm start`: launch Hopper and run the built stdio MCP server.
|
||||
|
||||
See [docs/testing.md](docs/testing.md) for test scopes and verification lanes and [CONTRIBUTING.md](CONTRIBUTING.md) for contribution checks. Pre-commit formats and lints staged source; pre-push runs `npm run check:fast`.
|
||||
Pre-commit hooks via Husky run `oxlint` on staged files before every commit. Use `npm run lint:fix` to auto-correct violations locally.
|
||||
|
||||
## Configuration & Environment Variables
|
||||
|
||||
Configuration is parsed and validated by `src/config.ts` and `src/config/`. Keep user-facing setup and provider configuration in [README.md](README.md) and the relevant guide under `docs/`; do not maintain a second environment-variable catalogue here.
|
||||
- `HOPPER_TARGET_PATH` (optional): absolute initial binary or `.hop` target. Target-free MCP sessions use `open_binary` instead.
|
||||
- `HOPPER_LAUNCHER_PATH` (optional): override the Hopper executable path (defaults to `/Applications/Hopper Disassembler.app/Contents/MacOS/hopper`).
|
||||
- `HOPPER_TARGET_KIND` (optional, default `executable`): startup kind for `HOPPER_TARGET_PATH`; dynamic targets are classified from their paths and headers.
|
||||
- `HOPPER_LOADER_ARGS_JSON` (optional): JSON array overriding derived Hopper loader arguments for supported executable targets, e.g. `["-l","Mach-O","--aarch64"]`.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
Use ESM TypeScript, two-space indentation, and the committed Oxfmt configuration. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers.
|
||||
|
||||
## Boundary Contracts
|
||||
|
||||
Treat a boundary as a contract between the producer's actual representation and the consumer's required meaning. When implementing or auditing a boundary, trace the value through parsing, normalization, authorization, serialization, and the CLI/MCP result. Establish affected callers from their code paths; similar tool names or workflows do not prove that they share a schema or failure mode.
|
||||
|
||||
When changing boundary behavior, inspect adjacent input representations, failure paths, and affected callers, and correct the underlying assumption across those cases.
|
||||
|
||||
Keep portable evidence and scenario validation distinct from host-native execution checks. Absolute filesystem paths, file URLs, and HTTP paths have different semantics; do not substitute one platform's syntax for the domain concept. Interpret provider metadata according to its documented or observed producer behavior. Keep values used for identity, provenance, matching, and path resolution separate from display formatting. When normalization loses information, preserve the source value and an explicit unknown; display placeholders must not feed back into lookup or selection.
|
||||
|
||||
Preserve meaningful failure reasons through application and adapter layers. Malformed input is distinct from an unsupported target, unavailable provider, or host operating-system permission denial. Diagnostics should identify the failed constraint and the target or lifecycle request it applies to. Recovery advice must address that reason and point to an available workflow; generic catches must not erase actionable validation details. Preserve collected observations on execution failure independently of whether cleanup succeeds.
|
||||
|
||||
Leave meaningful target, action, capture, and output choices to the agent. A selected operation already expresses intent; do not require approval booleans or repeated permission declarations. Trace each setting to its consumer: remove ignored options and single-value confirmations, derive built-in lifecycle behavior, and supply defaults for omitted optional metadata. Report actual effects and limitations where they help interpret results rather than asking callers to restate them.
|
||||
|
||||
Verify the representation consumed at the next boundary. Internal validator success does not establish JSON Schema validity or client compatibility: validate advertised input and output schemas against their declared dialect after SDK conversion, and keep generated contracts aligned. Use representative producer data and regressions that exercise the failed behavior. Platform and provider support claims require the corresponding real workflow; package startup, capability probes, and mock transport tests establish only their narrower claims. Report unverified coverage explicitly.
|
||||
|
||||
Bind tool handlers to named contracts, preserving their exact input and output types. Catalog ordering is presentation metadata; changing that order must not select a different schema or operation for an existing handler.
|
||||
|
||||
## Designing MCP Tools
|
||||
|
||||
Start from the analyst question and desired result, not a provider API. Before adding a tool, inspect the existing contract and its nearest alternative.
|
||||
|
||||
Let agents compose experiments with ordinary commands, scripts, and local fixture servers. REA tools should perform the requested inspection or capture and return evidence. A custom orchestration language, replay engine, or separate prepare/execute plan needs an observed requirement that those primitives cannot satisfy. A plan-only tool without an executor does not establish runtime behavior; expose the useful inspection directly.
|
||||
|
||||
- Prefer reusable, composable primitives: inspect one explicit object or
|
||||
relationship and return evidence that can support different analyst
|
||||
questions. Compose primitives into a workflow only for a recurring outcome
|
||||
that benefits from joining evidence sources; keep one-application or
|
||||
business-domain interpretations out of general tool contracts.
|
||||
- Use **inspect/search** tools for facts about a target or candidate set; use **trace** tools for relationships; use **compare** tools for explicitly paired inputs.
|
||||
- Add a **workflow** when observed agent use shows a repeated sequence that REA can compose without losing analyst control. Return useful results inline so callers can choose their next action.
|
||||
- Use **observe/capture** only when runtime activity is required, and declare authority and lifecycle effects in the contract.
|
||||
- Extend an existing tool when intent and result contract are unchanged. Add a tool for a distinct analyst outcome or materially different authority.
|
||||
- Keep caller-facing names and results provider-neutral. Put engine-specific behavior in provider adapters and report each provider's exact coverage.
|
||||
- Keep results complete by default. Add a limit only when it follows from a real format, protocol, authority, or resource-safety constraint; explain truncation and unsupported facets. Account for representation expansion and products of independently bounded dimensions before allocating or retaining output. Derive facet completeness from what was examined and exhausted, not an empty failure list. Keep observed, derived, inferred, and unknown results distinct.
|
||||
- Include artifact identity, source locations, Evidence references, actionable errors, and relevant limitations when they affect conclusions. Return the evidence needed for the next analysis inline rather than requiring a resource or identifier lookup.
|
||||
- Implement shared application workflows behind CLI and MCP adapters. Update canonical contracts and generated catalog together.
|
||||
|
||||
See [docs/tool-design.md](docs/tool-design.md) for the design checklist. When usability or tool selection changes, evaluate representative CLI/MCP tasks as well as schema and transport behavior.
|
||||
Use ESM TypeScript, two-space indentation, and Prettier defaults. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers.
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
Name tests `*.test.ts`. Use Vitest and production seams (`tests/fixtures/`) rather than module mocks. Domain tests assert pure behavior; adapter tests use fake launcher/socket seams; MCP tests connect with the client SDK version pinned in `package.json`. Preserve the canonical tool inventory defined by `TOOL_CONTRACTS` and verified through `CATALOG_IDENTITY` and generated product metadata. Cover malformed input, cancellation, lifecycle cleanup, and actual format, protocol, host-permission, and target-identity boundaries. Do not add tests that merely freeze arbitrary caps or prescribed call sequences. Real Hopper, Ghidra, browser, managed conformance, and any real managed-tool claims cannot be replaced by mocks; use the corresponding `verify:*` command.
|
||||
|
||||
Keep tool catalogs complete and self-describing; prefer capability- and session-scoped availability over schema truncation. Serialized bytes alone do not measure agent usability or model context cost.
|
||||
|
||||
## Cursor Cloud specific instructions
|
||||
|
||||
Development requires Node.js 24.18.0 and npm 11.16.0 (`.nvmrc` and `packageManager`). The Cloud Agent image places an older `node` on `PATH` ahead of a normal install. Environment setup installs the pinned toolchain under `/usr/local` and prepends `/usr/local/bin` for login shells. Confirm `node -v` is `v24.18.0` before installing dependencies.
|
||||
|
||||
`npm ci` installs locked dependencies. `npm run build:cached` produces the CLI and MCP server. `npm run check:fast` is the pre-push typecheck and lint. Hopper, Ghidra, and IDA are optional bring-your-own providers. JavaScript analysis and the deterministic Vitest suites do not need them. `rea doctor` reports those engines as missing until they are configured.
|
||||
Name tests `*.test.ts`. Use Vitest and production seams (`tests/fixtures/`) rather than module mocks. Domain tests assert pure behavior; adapter tests use the fake launcher/socket; MCP tests connect with the beta.3 client. Preserve the 33 direct, 10 enhanced, 5 native, 2 artifact, and 18 session tool inventory (68 total). Cover malformed input, cancellation, timeouts, process exit, concurrency, limits, and clean shutdown. Real Hopper claims cannot be replaced by mocks — use `npm run verify:hopper` with two distinct binaries for end-to-end verification.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
Use Conventional Commit subjects because Release Please derives versions and changelogs from them. Examples: `feat: add historical source import`, `fix(process): stop timers after exit`, and `docs: update architecture`. Use `!` or a `BREAKING CHANGE:` footer for breaking changes. Pull request titles must follow the same format because squash merges use the title as the release commit. Pull requests should describe contract or behavior changes, list verification commands, link issues, and include sanitized MCP examples when schemas change. State whether real Hopper/Ghidra verification was performed. Never commit binaries, Hopper or Ghidra project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts (e.g. `.codex/`).
|
||||
|
||||
<!-- BEGIN:turborepo-agent-rules -->
|
||||
|
||||
# This is NOT the Turborepo you know
|
||||
|
||||
Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p "require.resolve('turbo/package.json')"` from a workspace that depends on `turbo`.
|
||||
|
||||
Read `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.
|
||||
|
||||
This block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `"agentGuidance": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.
|
||||
<!-- END:turborepo-agent-rules -->
|
||||
Use concise imperative subjects, optionally scoped, such as `Add Hopper timeout classification`. Pull requests should describe contract or behavior changes, list verification commands, link issues, and include sanitized MCP examples when schemas change. State whether real Hopper verification was performed. Never commit binaries, Hopper documents, credentials, `dist/`, `node_modules/`, or local planning artifacts (e.g. `.codex/`).
|
||||
|
||||
-1682
File diff suppressed because it is too large
Load Diff
+36
-117
@@ -2,110 +2,31 @@
|
||||
|
||||
REA welcomes focused bug fixes, documentation improvements, tests, and reverse-engineering workflow enhancements. Open an issue before a large contract or architecture change so its scope can be agreed before implementation.
|
||||
|
||||
For capability organization and provider composition, follow the incremental
|
||||
[migration guide](docs/capability-migration.md). Run `npm run verify:test-discovery`
|
||||
after adding or moving tests.
|
||||
|
||||
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
|
||||
|
||||
## Development setup
|
||||
|
||||
REA development requires Node.js 24.18.x and npm 11.16.x (pinned toolchain via `nvm use`; the supported runtime range is Node.js ^22.19 || ^24.11 || >=26, as the README badge states). Real-Hopper verification additionally requires either macOS 12+ or an officially supported Linux host (Ubuntu 24.04+, Fedora 41+, 64-bit Arch, or CachyOS) and an installed Hopper application. Linux demo verification uses its own private Xvfb display and does not require a desktop session. Run `nvm use` before installing dependencies.
|
||||
REA requires macOS 12 or newer, Node.js 24.18.x, npm 11.16.x, and a separately installed Hopper Disassembler application for real-Hopper verification. Run `nvm use` before installing dependencies.
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm run check:fast
|
||||
npm run rebuild:native # only when the packaged PTY binary is incompatible
|
||||
npm run build
|
||||
npm test
|
||||
```
|
||||
|
||||
`npm ci` installs the exact dependencies and prepares the Husky hooks without
|
||||
building the project. Run `npm run build:cached` when you need the standalone
|
||||
CLI or MCP server; `npm run build` remains the uncached compiler leaf used by
|
||||
Turbo. Turbo caches deterministic builds and static checks across Git
|
||||
worktrees. After a package, lockfile, or managed-skill version change, run
|
||||
`npm run metadata:generate` before building.
|
||||
Keep dependencies flowing inward through the existing domain, Hopper, application, server, and adapter layers. Parse unknown values at process and protocol boundaries, model expected failures with `Result`, and preserve the 31 direct, 8 enhanced, and 3 session tools unless a deliberate contract change updates every verifier and snapshot.
|
||||
|
||||
Keep dependencies flowing inward through the existing domain, contracts, provider, application, server, and adapter layers. Parse unknown values at process and protocol boundaries, model expected failures with `Result`, and preserve the canonical tool inventory defined by `TOOL_CONTRACTS` unless a deliberate contract change updates every verifier, generated catalog artifact, and snapshot. Keep tool discovery complete and report capability- and session-scoped availability through `binary_session`.
|
||||
|
||||
## Documentation website
|
||||
|
||||
The VitePress site uses the Markdown files in `docs/`. Run `npm run docs:dev`
|
||||
for live editing, `npm run docs:build` to check the production build and
|
||||
links, and `npm run docs:preview` to preview that build at `/rea/`.
|
||||
|
||||
Site navigation lives in `docs/.vitepress/config.ts`. Keep links to guides
|
||||
relative so they work on GitHub and the website; link to repository files
|
||||
outside `docs/` using their full GitHub URLs. Generated reference documents
|
||||
use `npm run docs:generate`; `docs:build` generates them before building the website.
|
||||
|
||||
Pull requests run `npm run docs:check`. `.github/workflows/pages.yml` is a
|
||||
manual VitePress build and does not publish. The public site at
|
||||
<https://morluto.github.io/rea/> is published only by the manual website
|
||||
workflow on `main`. The repository's **Settings → Pages → Build and
|
||||
deployment → Source** must be set to **GitHub Actions** before the first
|
||||
deployment.
|
||||
|
||||
## Development feedback and PR verification
|
||||
|
||||
For an ordinary edit, run the relevant regression and cached static checks:
|
||||
Before submitting a pull request, run:
|
||||
|
||||
```bash
|
||||
npm run test:focused -- src/config.test.ts
|
||||
npm run check:fast
|
||||
npm run check
|
||||
npm run verify:package
|
||||
npm pack --dry-run
|
||||
```
|
||||
|
||||
`test:local` selects dirty source tests without building; explicit source test
|
||||
paths run even on a clean tree. `check:changed` adds source tests affected since
|
||||
the branch merge base with `origin/main`. Use `npm run test:changed -- --base
|
||||
REVISION` to choose another base. Changed-test selection follows the import
|
||||
graph and is feedback, not complete correctness evidence. Inspect relevant
|
||||
boundary and provider behavior explicitly; see [docs/testing.md](docs/testing.md).
|
||||
|
||||
Before handing off a PR, run the relevant tests, `npm run check`, and
|
||||
`npm run docs:check` when contracts or generated metadata change. Record which
|
||||
checks ran. CI owns complete deterministic tests and aggregate coverage. Use
|
||||
`npm run check:pr` for a deliberate full local gate on broad changes or when
|
||||
investigating CI failures; it is not required after each edit, rebase, or push.
|
||||
Packaging, setup, installation, or distribution changes also require
|
||||
`npm run verify:package` and `npm pack --dry-run`. Provider behavior changes
|
||||
require the matching real-provider `verify:*` lane.
|
||||
|
||||
`check:fast` runs cached typecheck and lint, reporting diagnostics on failure.
|
||||
`check` adds formatting, dead-code, and package-metadata freshness checks.
|
||||
Formatting uses Oxfmt and the committed `.oxfmtrc.json`; generated sources use
|
||||
the same configuration. Pre-commit formats and lints staged files; pre-push runs
|
||||
`check:fast`.
|
||||
`docs:check` builds and validates generated metadata for the current checkout.
|
||||
The product catalog (`docs/public/product-catalog.json`), portable managed
|
||||
conformance projections (`docs/verification/managed-conformance-*.json`), and
|
||||
packaged skill (`skills/`) are ignored build outputs. Edit skill instructions
|
||||
and references in `skill-src/`; the build adds catalog-dependent metadata to
|
||||
the packaged copy without rewriting authored files. The generated manifest
|
||||
commits to that exact packaged skill bundle. It is a portable projection of
|
||||
the deterministic managed verifier, not a record of optional real-provider runs.
|
||||
CI validates these outputs and retains them as artifacts instead of pushing
|
||||
generated-only commits onto feature branches. Reviewed source metadata such as
|
||||
`src/generatedPackageMetadata.ts` and `docs/error-contract.schema.json` remains
|
||||
tracked and checked for freshness. Do not commit ignored generated outputs.
|
||||
The build-generated product catalog contains documented facts and their provider
|
||||
identity, rather than full runtime schema hashes. The managed skill contains
|
||||
instructions and inventory metadata; doctor compares its installed files with
|
||||
the canonical bundle. Schema-only fixes should not change these outputs or the
|
||||
skill commitment in the conformance manifest. Runtime schema identity remains
|
||||
available through doctor and `binary_session`.
|
||||
Real-provider execution remains uncached; deterministic builds use Turbo.
|
||||
|
||||
Local `npm test` runs every deterministic Vitest project without coverage or
|
||||
retries. CI runs four coverage shards and merges JUnit and timing reports.
|
||||
CI cancels superseded PR runs and skips package, Windows, and full test lanes
|
||||
for documentation-only PRs. Coverage thresholds remain in `vitest.config.ts`.
|
||||
|
||||
Tests that need a temporary directory must use
|
||||
`createTestTempDirectory` from `tests/fixtures/temporaryDirectory.ts`. The
|
||||
helper binds exact-path, awaited cleanup to the current Vitest case, including
|
||||
failure and timeout completion. Run `npm run verify:test-temp-hygiene` to build
|
||||
REA, execute the complete suite under a fresh `TMPDIR`, and reject any remaining
|
||||
REA-owned temporary path. Never add a glob cleanup for shared `/tmp/rea-*`
|
||||
content.
|
||||
`npm install` prepares the Husky pre-commit hook. Commits format and lint staged
|
||||
files with lint-staged, then typecheck the complete project. The normal test and
|
||||
CI coverage runs enforce the thresholds in `vitest.config.ts`; `npm run
|
||||
lint:dead` rejects unused files, exports, and dependencies.
|
||||
|
||||
Set `REA_LOG_LEVEL` to `trace`, `debug`, `info`, `warn`, `error`, `fatal`, or
|
||||
`silent` to control structured JSON diagnostics. MCP mode defaults to `info` and
|
||||
@@ -114,38 +35,36 @@ logging is opt-in and writes to stdout when a level is configured, preserving
|
||||
machine-readable command output by default. Request arguments, bridge
|
||||
authentication tokens, and environment data are redacted.
|
||||
|
||||
Changes that claim real Hopper behavior must also be tested against the
|
||||
source-owned, digest-bound conformance manifest. The verifier builds the
|
||||
platform-native fixtures before starting Hopper:
|
||||
Changes that claim real Hopper behavior must also be tested against two distinct binaries:
|
||||
|
||||
```bash
|
||||
HOPPER_TARGET_PATH=/path/to/target-a \
|
||||
HOPPER_SECOND_TARGET_PATH=/path/to/distinct-target-b \
|
||||
npm run verify:hopper
|
||||
```
|
||||
|
||||
On a self-hosted Linux runner with the setup-installed Xvfb dependencies, use:
|
||||
|
||||
```bash
|
||||
npm run verify:hopper:linux
|
||||
```
|
||||
|
||||
Set `REA_HOPPER_CONFORMANCE_MANIFEST_PATH` only to verify another source-built
|
||||
manifest. The normal commands use `build/conformance/manifest.json`; generated
|
||||
fixtures and manifests remain ignored and must not be committed.
|
||||
|
||||
The macOS and Linux real-Hopper workflows remain separate so a successful mock or package test cannot be reported as platform-runtime proof. Pull requests changing setup, launch, bridge, or Hopper behavior must state which real workflows ran and why either workflow was unavailable.
|
||||
|
||||
Describe the behavior change and verification performed in the pull request. Never commit binaries, Hopper documents, credentials, `dist/`, `node_modules/`, or local planning artifacts.
|
||||
|
||||
## Maintainer release checklist
|
||||
|
||||
Use the [checkpoint release guide](docs/releasing.md). Releases start from an
|
||||
explicit `release/VERSION` branch cut at a recorded commit. Manually prepare
|
||||
the bot PR, wait for its exact-head CI and review, merge into that frozen
|
||||
branch, and manually publish through the official Release workflow. Main
|
||||
pushes do not update release PRs or publish packages. Both publishers build
|
||||
the exact SHA tagged by Release Please.
|
||||
Run the full checks, isolated package verifier, package dry run, and two-target real-Hopper verifier described above. Build a local tarball and exercise the executable through the package boundary:
|
||||
|
||||
Keep new implementation commits on main for the next release. The workflow
|
||||
owns packaged-artifact verification, npm publication, the published CLI/MCP
|
||||
canary, and MCP Registry publication. Sync release metadata back to main after
|
||||
publication; see the guide for partial-publication recovery and verification.
|
||||
```bash
|
||||
npm pack
|
||||
npm exec --yes --package ./morluto-rea-0.1.0.tgz -- rea --help
|
||||
```
|
||||
|
||||
Publish the public scoped package:
|
||||
|
||||
```bash
|
||||
npm publish --access public
|
||||
```
|
||||
|
||||
After npm registry propagation, verify the published CLI and connect a beta.3 MCP client to the published server to confirm all 42 tools:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents --help
|
||||
npx -y rea-agents doctor
|
||||
npx -y rea-agents setup --yes
|
||||
npx -y rea-agents mcp
|
||||
```
|
||||
|
||||
@@ -4,334 +4,351 @@
|
||||
|
||||
# REA: Reverse Engineer Anything
|
||||
|
||||
### One MCP for reverse engineering across binaries, applications, and runtime behavior.
|
||||
### One CLI and MCP server for coding agents to reverse engineer anything
|
||||
|
||||
**See a feature you like. Understand how it works, down to the binary level.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](docs/mcp-contracts.md#generated-catalog)
|
||||
[](https://nodejs.org/)
|
||||
[](https://skills.sh/morluto/rea/reverse-engineer-anything)
|
||||
[](#68-tools-for-investigation)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Website](https://morluto.github.io/rea/) · [Guides](https://morluto.github.io/rea/guides/) · [Showcases](https://morluto.github.io/rea/showcase/)**
|
||||
|
||||
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [FAQ](#faq) · [Documentation](#documentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
[Quick start](#quick-start) · [Current status](#current-status) · [Investigation model](#the-investigation-model) · [68 tools](#68-tools-for-investigation) · [Roadmap](#roadmap) · [How it works](#how-it-works)
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA launching its analysis bridge inside Hopper while inspecting a native binary" width="1200" />
|
||||
|
||||
<br />
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>Join the Reverse Engineering Community</strong>
|
||||
</a><br />
|
||||
<sub>Discord · Q&A · Show and Tell</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<code>npx skills add morluto/rea</code>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
See a feature in an app that you want in your own product? Ask your agent to investigate it with REA. It can inspect the app without its source code, explain how the feature works, show the evidence, and build a version for your project.
|
||||
See a feature in an app that you want in your own product? Give the app to your coding agent—even without its source code. With REA, the agent can investigate the feature, explain how it works, show its evidence, and build a version adapted to your stack and requirements.
|
||||
|
||||
REA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps, .NET assemblies, and websites. You can also use the same tools from your terminal. Analysis runs locally, and results include the evidence and limitations behind each conclusion.
|
||||
REA gives agents one consistent way to investigate software. Today that includes deep native analysis through Hopper, complete function dossiers, reproducible Evidence v2 records, and controlled process capture. The longer-term toolkit extends the same agent workflow to packaged apps, JavaScript bundles, websites, APIs, protocols, mobile artifacts, firmware, runtime behavior, and differences between versions.
|
||||
|
||||
Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper or Ghidra installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs neither engine.
|
||||
Reverse engineering normally makes the operator choose a tool, learn its API, move evidence between programs, and decide what to inspect next. REA gives that work to the agent through commands, skills, structured results, and repeatable investigation workflows.
|
||||
|
||||
> **[Visit the REA website](https://morluto.github.io/rea/)** for setup instructions, illustrated guides, and real case studies.
|
||||
## Just ask your agent
|
||||
|
||||
Install the REA skill:
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
Then ask:
|
||||
|
||||
```text
|
||||
Set up REA and reverse engineer the Notes app. Explain how search works,
|
||||
show me how you know, and build a similar feature for my project.
|
||||
```
|
||||
|
||||
Notes is only an example. Name any app you want to understand, or ask the agent to start with an overview.
|
||||
|
||||
## The investigation model
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%" valign="top">
|
||||
<strong>Decompile</strong><br /><br />
|
||||
Open an app and recover readable code, strings, names, and other clues about how it works.
|
||||
</td>
|
||||
<td width="33%" valign="top">
|
||||
<strong>Understand</strong><br /><br />
|
||||
Follow the code from one part of the app to another until the agent can explain how a feature actually works.
|
||||
</td>
|
||||
<td width="33%" valign="top">
|
||||
<strong>Recreate</strong><br /><br />
|
||||
Turn what the agent learned into a feature for your own product, adapted to your stack, interface, and requirements.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
REA shows how it reached its conclusions. It does not claim to recover original source code or automatically clone an application.
|
||||
|
||||
## Why REA
|
||||
|
||||
| | |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------ |
|
||||
| **Built for agents** | Ask what an app does and let your agent inspect it instead of guessing. |
|
||||
| **CLI and MCP** | Run the same reverse-engineering capabilities from your terminal or coding agent. |
|
||||
| **Complexity handled** | REA installs and manages the reverse-engineering tools behind the scenes. |
|
||||
| **From insight to code** | Understand a feature, then build your own version in the same coding session. |
|
||||
| **Local by design** | Analysis runs on your Mac. REA does not upload the app to a hosted analysis service. |
|
||||
| **Keeps context** | Investigate several apps without starting over for every question. |
|
||||
|
||||
## Quick start
|
||||
|
||||
### Set up your agent
|
||||
|
||||
With Node.js and npm installed, run:
|
||||
### With a coding agent — recommended
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
Choose your agents, review the proposed changes, and approve them. Setup adds
|
||||
REA's MCP server and matching workflow instructions, with backups of existing
|
||||
configuration. Restart your agent afterward.
|
||||
Ask your agent to set up REA. It will check your Mac, explain anything it needs to install, ask for approval, and guide you through system prompts. After setup, restart the agent if it asks you to load the full REA toolset.
|
||||
|
||||
Setup supports Claude Code, Codex, Cursor, Gemini CLI and
|
||||
[other agents](docs/installation.md#supported-agents). See
|
||||
[installation and setup](docs/installation.md) for provider configuration and
|
||||
manual MCP registration.
|
||||
### From Terminal
|
||||
|
||||
### Ask your agent
|
||||
```bash
|
||||
npx -y rea-agents setup --yes
|
||||
```
|
||||
|
||||
If macOS or an installer asks for confirmation, complete the prompt and run the same command again. Restart a configured coding agent so it loads REA.
|
||||
|
||||
### What setup handles
|
||||
|
||||
- macOS 12 or newer
|
||||
- Node.js 24.18.x with npm 11.16.x (`nvm use` selects the pinned version)
|
||||
|
||||
If process capture reports that its native PTY backend is unavailable, install Xcode command-line tools and run `npm run rebuild:native`. Linux source builds require Python, `make`, and a C++ toolchain. Compatible packaged binaries do not require this rebuild.
|
||||
|
||||
Process capture is disabled by default. Enabling it requires
|
||||
`REA_PROCESS_CAPTURE_ENABLED=true`, approved executable and working roots in
|
||||
`REA_PROCESS_EXECUTABLE_ROOTS_JSON` and `REA_PROCESS_WORKING_ROOTS_JSON`, and an
|
||||
environment allowlist in `REA_PROCESS_ALLOWED_ENV_JSON`. The current PTY adapter
|
||||
uses host networking, so it additionally requires the explicit operator grant
|
||||
`REA_PROCESS_ALLOW_EXTERNAL_NETWORK=true`. Without that grant the adapter
|
||||
refuses to launch; loopback replay alone does not constitute network isolation.
|
||||
|
||||
You do not need to install the reverse-engineering tools manually. Setup installs Homebrew and [Hopper](https://www.hopperapp.com/) when needed, configures detected Claude Desktop and Cursor installations, and installs the REA skill. Hopper is separate software and requires its own license; setup installs it but does not provide a license.
|
||||
|
||||
If something is not working, run:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents doctor
|
||||
```
|
||||
|
||||
### CLI or coding agent?
|
||||
|
||||
| If you want to… | Use |
|
||||
| --------------------------------------------------------- | ---------------------------------------------- |
|
||||
| Ask an agent to investigate an app and build a feature | Install the skill, then talk to your agent |
|
||||
| Inspect or decompile one part of an app from the Terminal | `rea analyze` or `rea decompile` |
|
||||
| Validate or canonicalize an Evidence v2 bundle | `rea evidence-import` or `rea evidence-export` |
|
||||
| Import source as historical reference | `rea import-reference-source` |
|
||||
|
||||
Filesystem evidence commands and MCP file tools are disabled until the operator approves absolute roots:
|
||||
|
||||
```bash
|
||||
export REA_EVIDENCE_ROOTS_JSON='["/absolute/path/to/evidence"]'
|
||||
rea evidence-import /absolute/path/to/evidence/bundle.json
|
||||
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
|
||||
```
|
||||
|
||||
Historical source import requires a separate allowlist and never treats source as current behavioral authority:
|
||||
|
||||
```bash
|
||||
export REA_REFERENCE_ROOTS_JSON='["/absolute/path/to/source"]'
|
||||
rea import-reference-source /absolute/path/to/source
|
||||
```
|
||||
|
||||
Exports never replace an existing file unless `--overwrite` is explicit. Imports are size/depth bounded, validate every Evidence v2 ID and manifest, and never execute bundle content.
|
||||
|
||||
## One prompt, a full investigation
|
||||
|
||||
```text
|
||||
Understand how search works in the Notes app, show me the evidence, and build a
|
||||
similar feature for my project.
|
||||
Reverse engineer the Notes app. Find how offline search works, explain it,
|
||||
and build a version for my project using TypeScript and SQLite.
|
||||
```
|
||||
|
||||
Replace Notes with your target app and the feature you want to understand.
|
||||
REA gives the agent a clear path from that request to working code:
|
||||
|
||||
### Use the terminal
|
||||
| Step | What the agent does | REA tools |
|
||||
| ---: | --------------------------------------- | ---------------------------------------------------------------- |
|
||||
| 1 | Opens and identifies the binary | `open_binary`, `binary_overview` |
|
||||
| 2 | Finds likely offline-search clues | `search_strings`, `search_procedures`, `list_names` |
|
||||
| 3 | Connects those clues to executable code | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | Reconstructs the relevant control flow | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 5 | Decompiles the relevant routines | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | Builds the feature in your project | code adapted to your stack, product, and requirements |
|
||||
|
||||
Inspect an extracted JavaScript/Electron app directory or ASAR:
|
||||
REA handles the app analysis in steps 1–5. The agent performs step 6 with its normal file-editing and test tools, using what it learned about the app.
|
||||
|
||||
## What agents can do
|
||||
|
||||
- Investigate a feature you like and build a version tailored to your own product.
|
||||
- Explain how a feature works when its source code is unavailable.
|
||||
- Reconstruct an app's authentication, storage, update, or networking flow.
|
||||
- Recover enough structure to document an undocumented format or interface.
|
||||
- Trace a suspicious behavior from a string or symbol to the code that implements it.
|
||||
- Compare implementation paths across two app versions by switching targets in one session.
|
||||
- Turn recovered behavior into product features, tests, migration notes, ports, or interoperable replacements.
|
||||
- Analyze Swift and Objective-C metadata without manually untangling every mangled symbol.
|
||||
- Leave names, comments, and bookmarks in Hopper so human and agent analysis reinforce each other.
|
||||
|
||||
## 68 tools for investigation
|
||||
|
||||
| Tool family | Count | Examples |
|
||||
| ------------------------- | ----: | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native inspection | 33 | procedures, pseudocode, assembly, strings, names, segments, callers, callees, xrefs, annotations |
|
||||
| Investigation workflows | 10 | `binary_overview`, `analyze_function`, `batch_decompile`, `trace_feature`, call graphs, Swift and Objective-C discovery |
|
||||
| Native macOS utilities | 5 | Mach-O metadata, code signatures, plists, architectures, Swift demangling; Hopper-free and provenance-bearing |
|
||||
| Artifact graph | 2 | deterministic directory, ZIP/APK/IPA, and ASAR inventory; explicitly selected extraction into an absent owned tree |
|
||||
| Workspace and observation | 18 | target lifecycle, Evidence v2 bundles, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
|
||||
|
||||
The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness implements controlled behavioral capture.
|
||||
|
||||
## Current status
|
||||
|
||||
REA is already useful for native application investigation on macOS:
|
||||
|
||||
- Open Mach-O, ELF, PE, `.app`, ZIP, APK, IPA, ASAR, plist, JavaScript, source-map, and Hopper database targets.
|
||||
- Traverse content-addressed artifact graphs without extraction; materialize only approved occurrences into absent output roots.
|
||||
- Build bounded function dossiers with pseudocode, assembly, CFG edges, comments, calls, references, strings, and names.
|
||||
- Search and trace features across symbols, strings, metadata, references, and call paths.
|
||||
- Record every successful result as deterministic Evidence v2 with artifact and provider identity, confidence, authority, limitations, and locations.
|
||||
- Export and import evidence bundles across sessions.
|
||||
- Capture approved PTY scenarios, child processes, filesystem changes, and loopback HTTP/WebSocket exchanges, then compare normalized captures.
|
||||
- Compare complete artifact inventories by stable path, content, metadata, and relations; incomplete evidence never implies equivalence.
|
||||
- Compare explicit function dossiers across text, calls, references, strings, and address-normalized CFG topology with per-facet unknowns.
|
||||
- Compare canonical Evidence bundles by exact membership, explicit observation pairs, and residual-unknown histories without turning omissions into behavioral absence.
|
||||
- Aggregate runtime comparisons into observed behavior changes while keeping static artifact/function differences labeled as candidates.
|
||||
- Build bounded, Evidence-cited direct call paths by exact address without treating missing dossiers as graph leaves.
|
||||
- Correlate exact static/runtime findings through explicit hypotheses without claiming causality from cochange.
|
||||
- Verify finite behavioral and structural reconstruction specifications with pass, fail, and unknown kept distinct.
|
||||
- Track residual unknowns through immutable CAS revisions, evidence-qualified resolution, contradictions, probes, and validated dependency relationships.
|
||||
- With explicit `unknown_registry_approved: true`, record bounded trace/capture residuals, typed provider unavailability, and capture disagreements automatically.
|
||||
|
||||
Hopper is the first provider, not the boundary of the project. Some current workflows still require Hopper and macOS; every evidence record identifies the provider and limitations behind its result.
|
||||
|
||||
## Roadmap
|
||||
|
||||
REA is growing into a toolkit for understanding software across static artifacts and observed behavior. The next capability families are:
|
||||
|
||||
1. **Artifact decomposition** — DMG, ASAR, ZIP, packages, universal-binary slices, application resources, embedded frameworks, mobile packages, and artifact graphs.
|
||||
2. **Web and Electron investigation** — Playwright/CDP capture of DOM, accessibility trees, screenshots, storage, console, IPC, HTTP, WebSocket, routes, and visual or structural differences.
|
||||
3. **Deterministic behavior harnesses** — stronger process-tree ownership, protocol fixtures, network policy, filesystem tracing, signals, reconnects, and cross-version comparison.
|
||||
4. **JavaScript and source recovery** — bundle indexing, AST/module reconstruction, source-map discovery, historical-source matching, and CodeDB-backed cross-references.
|
||||
5. **Runtime observation** — approval-gated LLDB, Frida, system logs, process and filesystem observers, and native API tracing.
|
||||
6. **More static-analysis providers** — native platform utilities first, followed by Ghidra, IDA/Hex-Rays, Binary Ninja, Rizin, LIEF, and other engines behind provider-neutral capabilities.
|
||||
7. **More targets and platforms** — Windows-native providers and ConPTY verification, Linux parity, websites and APIs, mobile artifacts, firmware, document formats, and other software-defined systems.
|
||||
8. **Differential reconstruction** — compare artifacts, functions, bundles, protocols, UIs, and process captures; track residual unknowns; verify a reconstruction against observed behavior.
|
||||
|
||||
Roadmap items describe direction, not shipped support. New providers must produce the same evidence and safety metadata as existing capabilities before they become part of the public workflow.
|
||||
|
||||
## Using REA with other coding agents
|
||||
|
||||
Setup currently configures Claude Desktop and Cursor automatically. Any coding agent that supports local MCP servers can use REA with the configuration below.
|
||||
|
||||
### Manual MCP configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["Coding agent"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal --> REA
|
||||
REA --> Workspace["Investigation workspace<br/>evidence + artifacts + captures"]
|
||||
Workspace --> Router["Capability router"]
|
||||
Router --> Hopper["Hopper provider"]
|
||||
Router --> Native["Native macOS provider"]
|
||||
Router --> Artifact["Artifact graph provider"]
|
||||
Router --> Process["Process capture provider"]
|
||||
Router -. roadmap .-> More["Browser, dynamic,<br/>and additional static providers"]
|
||||
Hopper --> Target["Target software"]
|
||||
Process --> Target
|
||||
Native --> Target
|
||||
Artifact --> Target
|
||||
```
|
||||
|
||||
The CLI and MCP server use the same application workflows and evidence contracts. A provider declares which capabilities it supports and the side effects those capabilities may have. Terminal commands are short-lived; an MCP session can retain an active target and evidence ledger across an investigation.
|
||||
|
||||
## CLI
|
||||
|
||||
The agent workflow above is the easiest way to use REA. For a one-off overview from the Terminal:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
npx -y rea-agents analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
The result includes modules, imports, Electron boundaries and their evidence.
|
||||
Replace the path with your target, such as `"D:/apps/example"` on Windows.
|
||||
Run `npx -y rea-agents --help` for direct decompilation and other options.
|
||||
|
||||
To install the `rea` command for regular use:
|
||||
Or install the `rea` command globally:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea --help
|
||||
rea mcp
|
||||
```
|
||||
|
||||
For native analysis, configure a provider first. See the
|
||||
[CLI and Evidence guide](docs/cli.md) for native commands, provider selection,
|
||||
snapshots and scripting.
|
||||
REA accepts a Mac `.app` folder directly. If an agent cannot find an app by name, tell it where the app is installed.
|
||||
|
||||
### Update REA
|
||||
## Current Hopper provider
|
||||
|
||||
REA changes quickly, and new releases include frequent bug fixes. Keep your
|
||||
installation up to date.
|
||||
REA starts Hopper when needed; Hopper does not need to be running first. Hopper's launcher internally activates the application, so opening a target may bring Hopper to the foreground. REA asks macOS to start Hopper hidden and in the background when possible, but cannot guarantee that it will remain behind the current application.
|
||||
|
||||
For an npm-installed CLI:
|
||||
REA derives explicit format and architecture arguments to prevent common FAT and ARM selection dialogs. Other Hopper or macOS dialogs may still require a person. REA reports timeouts and remediation through CLI or MCP results instead of attempting to answer UI prompts.
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
Closing a REA session shuts down its bridge and removes its private socket directory. It does not quit a Hopper application the user may be using.
|
||||
|
||||
To refresh your agent registrations and skill, run the setup command printed
|
||||
by the update.
|
||||
## Security model
|
||||
|
||||
If you use `npx`, update your agent setup with:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
Review the setup changes and restart your agent. For one-off CLI commands,
|
||||
use `npx rea-agents@latest` followed by the command.
|
||||
|
||||
## How REA works
|
||||
|
||||
Your agent calls REA through MCP to inspect the target and trace relevant code.
|
||||
REA returns findings with their evidence. The agent uses them to ask follow-up
|
||||
questions, explain the behavior, or write and test an implementation.
|
||||
CLI commands use the same workflows.
|
||||
|
||||

|
||||
|
||||
[Open the full-size figure](website/public/assets/figures/rea-investigation-flow.svg).
|
||||
|
||||
<a id="current-status"></a>
|
||||
|
||||
## What you can analyze
|
||||
|
||||
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+, plus npm.
|
||||
Additional tools and host support depend on the target:
|
||||
|
||||
| Target | What REA returns | Requirements and guide |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](https://morluto.github.io/rea/guides/native/) |
|
||||
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
|
||||
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier; [offline EVM guide](docs/evm-bytecode.md) |
|
||||
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools; optional GDB/pwndbg; [recorded crashes](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; [application analysis](https://morluto.github.io/rea/guides/javascript/) |
|
||||
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](https://morluto.github.io/rea/guides/browser/) |
|
||||
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR; mitmdump on Linux for native mitmproxy captures; [capture guide](docs/web-network-captures.md) |
|
||||
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | Static inspection; [managed-code guide](docs/managed-code-analysis.md) |
|
||||
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS; [Android guide](docs/android-analysis.md) |
|
||||
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
|
||||
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
|
||||
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
|
||||
|
||||
Static JavaScript and .NET inspection read the supplied files without running
|
||||
the application. Runtime capture runs or interacts with the selected target
|
||||
using your user permissions; each runtime guide describes its effects.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Native formats and host support vary by provider. See
|
||||
[Hopper and Ghidra setup](docs/installation.md#hopper), the
|
||||
[IDA guide](docs/ida-provider.md), and
|
||||
[experimental Windows Ghidra support](docs/windows-ghidra-p0.md).
|
||||
Ghidra also supports [16-bit DOS analysis](docs/ghidra-dos.md).
|
||||
For provider selection, see the [CLI guide](docs/cli.md#choose-a-provider).
|
||||
Check [release availability](docs/installation.md#released-package-and-main)
|
||||
for features added since the latest npm release.
|
||||
|
||||
## Showcases
|
||||
|
||||
### DX-Ball: reconstruct a sound-pan calculation
|
||||
|
||||
Follow a sound call into its position-to-pan helper, inspect the instructions,
|
||||
and turn incomplete pseudocode into C. The reconstruction passes 3,205
|
||||
original-x86 cases and reproduces all 63 compiled function bytes.
|
||||
|
||||
[Read the case study](https://morluto.github.io/rea/showcase/dx-ball/) ·
|
||||
[Reconstruction repository](https://github.com/N0zoM1z0/dx-ball)
|
||||
|
||||
### Notion: trace the Electron clipboard bridge
|
||||
|
||||
Find the renderer's clipboard API, follow it through preload and IPC into the
|
||||
main process, and inspect the rich clipboard format.
|
||||
|
||||
[Read the case study](https://morluto.github.io/rea/showcase/notion/)
|
||||
|
||||
### TH04: recover a DOS bullet-ring calculation
|
||||
|
||||
Inspect the original PC-98 game's 16-bit instructions, recover the fixed and
|
||||
aimed angle calculations, and compare the reconstructed C++ with the
|
||||
historical compiler output.
|
||||
|
||||
[Read the case study](https://morluto.github.io/rea/showcase/th04/) ·
|
||||
[Reconstruction repository](https://github.com/N0zoM1z0/th04)
|
||||
|
||||
If you've used REA on something interesting, we'd love to see it. Share your
|
||||
case in an [issue](https://github.com/morluto/rea/issues) or a
|
||||
[pull request](https://github.com/morluto/rea/pulls), including the target,
|
||||
your question, how REA helped, and what you found.
|
||||
REA does not provide a hosted analysis service. Hopper communication uses an authenticated private local socket. Dynamic capabilities are disabled by default and require both operator policy and explicit per-call approval. REA is not a security sandbox: providers and launched targets run with the current user's permissions, and each capability reports its side effects and limitations. Report vulnerabilities through the private process in [SECURITY.md](SECURITY.md).
|
||||
|
||||
## FAQ
|
||||
|
||||
<details>
|
||||
<summary><strong>Does Hopper need to be running before I start REA?</strong></summary>
|
||||
|
||||
No. REA starts Hopper when an operation needs it. An already-running Hopper application is also supported.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Why did Hopper appear in front of my other windows?</strong></summary>
|
||||
|
||||
Hopper's launcher internally activates the application. REA requests background startup, but macOS and Hopper may still bring a window or dialog forward. See [Hopper application behavior](#hopper-application-behavior).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA include Hopper?</strong></summary>
|
||||
|
||||
No. Setup can install Hopper for you, but Hopper remains separate software with its own license. REA supplies the CLI, MCP server, and workflows that make it usable by agents.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA upload the app?</strong></summary>
|
||||
|
||||
REA has no hosted analysis service. Current providers analyze artifacts and capture behavior locally. Your coding agent or model provider may have its own data policy, so review that separately.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Can REA recover the original source code?</strong></summary>
|
||||
|
||||
No decompiler can guarantee the original source. REA gives an agent pseudocode, assembly, symbols, strings, metadata, and relationships that it can use to explain or compatibly recreate observed behavior.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Which agents can use REA?</strong></summary>
|
||||
|
||||
Any agent that supports local MCP servers. Setup configures the
|
||||
[supported agents](docs/installation.md#supported-agents); other clients can use
|
||||
[manual MCP registration](docs/installation.md#mcp-registry).
|
||||
Any coding agent that can run a local MCP server can use the manual configuration. Setup currently detects and configures Claude Desktop and Cursor automatically.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Do I need Hopper, Ghidra or IDA?</strong></summary>
|
||||
## Development
|
||||
|
||||
Deep native analysis uses one of them. Static JavaScript and .NET inspection
|
||||
work without a native analysis engine. Setup can install Hopper after approval;
|
||||
Ghidra and IDA use your existing installations. See [provider setup](docs/installation.md#hopper).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Do I need to start Hopper first?</strong></summary>
|
||||
|
||||
REA starts Hopper when an operation needs it. On macOS, a first-run dialog may
|
||||
ask you to choose demo mode or activate your license. See
|
||||
[Hopper startup and troubleshooting](docs/installation.md#launcher-paths-and-troubleshooting).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What does installing the skill from skills.sh do?</strong></summary>
|
||||
|
||||
The skill supplies investigation instructions for your agent. Use `rea setup`
|
||||
to register REA's MCP server and install the matching instructions, then restart
|
||||
your agent. See [skill-only installation](docs/installation.md#skill-only-installation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What code does REA return?</strong></summary>
|
||||
|
||||
Native analysis returns pseudocode and assembly. JavaScript/Electron analysis
|
||||
recovers modules and their relationships. Your agent uses these findings to
|
||||
write and test an implementation; the [showcases](#showcases) give worked examples.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Does REA upload my app?</strong></summary>
|
||||
|
||||
REA analyzes targets locally. Your agent receives the tool results, and its
|
||||
model provider has its own data policy.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What should I do if I hit a bug?</strong></summary>
|
||||
|
||||
Update first; a recent release may already fix it.
|
||||
|
||||
For an npm-installed CLI:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
For agent setup through `npx`:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
If you're using an agent, complete the [setup refresh](#update-rea) and restart
|
||||
it. Retry the same task. If the problem persists, [open an issue](https://github.com/morluto/rea/issues)
|
||||
with your REA version, target type, steps to reproduce and error output.
|
||||
|
||||
</details>
|
||||
|
||||
## Documentation
|
||||
|
||||
Start with the website's [worked guides](https://morluto.github.io/rea/guides/).
|
||||
For exact options, prerequisites and result contracts:
|
||||
|
||||
- [Installation and setup](docs/installation.md): agent registration, provider configuration, updates and uninstall.
|
||||
- [Readiness and troubleshooting](docs/installation.md#check-readiness-for-your-task): diagnose one agent or analysis engine.
|
||||
- [CLI and Evidence](docs/cli.md): commands, provider selection, snapshots, import/export and exit statuses.
|
||||
- [MCP contracts](docs/mcp-contracts.md) and [agent prompts](docs/mcp-prompts.md): tool results, sessions and guided investigations.
|
||||
- [Tool catalog](docs/mcp-contracts.md#generated-catalog): build-generated inventory of tools, providers and CLI commands.
|
||||
- [Roadmap](docs/roadmap.md): planned work and capability trackers.
|
||||
|
||||
Report vulnerabilities through [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
We'd love your help with REA! [Open an issue](https://github.com/morluto/rea/issues) to
|
||||
report a bug or suggest a feature, or [send a pull request](https://github.com/morluto/rea/pulls)
|
||||
to improve the code or docs.
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks,
|
||||
[testing](docs/testing.md) for verification lanes, and the
|
||||
[architecture map](docs/architecture.mermaid) for the project structure.
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture, tests, and release instructions. Generated API documentation is available under [`docs/api`](docs/api/index.html).
|
||||
|
||||
## Project links
|
||||
|
||||
[Website](https://morluto.github.io/rea/) · [npm](https://www.npmjs.com/package/rea-agents) · [skills.sh](https://skills.sh/morluto/rea/reverse-engineer-anything) · [Issues](https://github.com/morluto/rea/issues) · [Security](SECURITY.md)
|
||||
|
||||
## Star history
|
||||
|
||||
🎉 **20,000 GitHub stars — thank you!**
|
||||
|
||||
Thanks to everyone using REA, reporting bugs, testing builds, and contributing fixes.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA GitHub star history" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
REA provides tools for lawful reverse-engineering research, analysis, and reconstruction. You are responsible for obtaining any required authorization and complying with applicable laws. The project does not endorse illegal or unauthorized use.
|
||||
[npm](https://www.npmjs.com/package/rea-agents) · [Issues](https://github.com/morluto/rea/issues) · [Security](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Hopper](https://www.hopperapp.com/)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+75
-169
@@ -2,173 +2,107 @@
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **العربية**
|
||||
|
||||
# REA: اعكس هندسة أي شيء
|
||||
# REA: هندسة أي شيء عكسيًا
|
||||
|
||||
### وسيط MCP واحد للهندسة العكسية عبر الملفات التنفيذية والتطبيقات وسلوك وقت التشغيل.
|
||||
### واجهة CLI وخادم MCP واحدان يمكّنان وكلاء البرمجة من إجراء هندسة عكسية لأي شيء
|
||||
|
||||
**اعثر على ميزة تعجبك. افهم آلية عملها. ابنها بالطريقة التي تريدها.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#منصة-أدوات-التحقيق)
|
||||
[](https://nodejs.org/)
|
||||
[](#منصة-من-68-أداة)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[الموقع (بالإنجليزية)](https://morluto.github.io/rea/) · [أدلة الاستخدام](https://morluto.github.io/rea/guides/) · [دراسة حالة DX-Ball](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[البدء السريع](#البدء-السريع) · [حالة الدعم](#حالة-الدعم) · [من الملف التنفيذي إلى السلوك](#من-الملف-التنفيذي-إلى-السلوك) · [منصة أدوات التحقيق](#منصة-أدوات-التحقيق) · [خطة العمل](#خطة-العمل) · [كيف يعمل؟](#كيف-يعمل)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>انضم إلى مجتمع الهندسة العكسية</strong>
|
||||
</a><br />
|
||||
<sub>Discord · أسئلة وأجوبة · شارك أعمالك</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
[البدء السريع](#البدء-السريع) · [من الملف التنفيذي إلى السلوك](#من-الملف-التنفيذي-إلى-السلوك) · [68 أداة](#منصة-من-68-أداة) · [كيف يعمل](#كيف-يعمل) · [الأسئلة الشائعة](#الأسئلة-الشائعة)
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA يشغّل جسر التحليل داخل Hopper لفحص ملف تنفيذي أصلي" width="1200" />
|
||||
<code>npx skills add morluto/rea</code>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
هل وجدت في تطبيق ما ميزة تريدها في منتجك؟ أعط التطبيق إلى وكيل البرمجة حتى من دون شيفرته المصدرية. باستخدام REA، يستطيع الوكيل استقصاء الميزة وفهم آلية عملها ثم بناء نسخة ملائمة لتقنياتك وتصميمك ومتطلباتك.
|
||||
|
||||
هل وجدت في تطبيق ميزة تريدها في منتجك؟ اطلب من وكيلك استقصاءها باستخدام REA. يستطيع فحص التطبيق من دون شيفرته المصدرية، وشرح طريقة عمل الميزة وعرض الأدلة، ثم بناء نسخة تناسب مشروعك.
|
||||
|
||||
يوفر REA أدوات لتحليل الملفات التنفيذية الأصلية وتطبيقات JavaScript وElectron وتجميعات .NET والمواقع. يمكنك استخدامها من وكيلك أو من الطرفية. يعمل التحليل محليًا، وتتضمن النتائج الأدلة والقيود.
|
||||
|
||||
يضبط Setup إعدادات الوكيل ويربطه بتثبيت موجود من Hopper أو Ghidra، ويثبّت سير عمل REA الموجّه. ويمكنه تثبيت Hopper بعد موافقتك. ولا يحتاج التحليل الثابت لتطبيقات JavaScript إلى أي محرك.
|
||||
يوفر REA هذه العملية عبر CLI وخادم MCP واحدين. يستطيع الوكيل قراءة شيفرة التطبيق وتتبع طريقة عمل الميزات ثم استخدام الأدلة مباشرة في عمله البرمجي المعتاد. ويتولى REA أدوات الهندسة العكسية المعقدة خلف واجهة واحدة.
|
||||
|
||||
## اطلب من وكيلك مباشرة
|
||||
|
||||
بعد [الإعداد](#البدء-السريع)، أعد تشغيل الوكيل ثم اطلب:
|
||||
|
||||
```text
|
||||
استقصِ طريقة عمل البحث في تطبيق Notes، واعرض الأدلة، ثم ابنِ ميزة مشابهة لمشروعي.
|
||||
```bash
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
استبدل Notes بالتطبيق الذي تريد فهمه، أو اطلب نظرة عامة أولًا.
|
||||
ثم اطلب:
|
||||
|
||||
```text
|
||||
أعد REA وأجر هندسة عكسية لتطبيق Notes. اشرح طريقة عمل البحث وكيف توصلت إلى ذلك،
|
||||
ثم ابنِ ميزة مشابهة لمشروعي.
|
||||
```
|
||||
|
||||
تطبيق Notes مجرد مثال. يمكنك تسمية أي تطبيق تريد فهمه أو طلب البدء بنظرة عامة.
|
||||
|
||||
## من الملف التنفيذي إلى السلوك
|
||||
|
||||
| فك الترجمة | الفهم | إعادة الإنشاء |
|
||||
| ---------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| فك الترجمة | الفهم | إعادة الإنشاء |
|
||||
| ----------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| استخرج الشيفرة شبه المصدرية، والتعليمات، والسلاسل النصية، والرموز | تتبع تدفق التحكم والمراجع المتبادلة والاستدعاءات والأنواع | حوّل ما تعلمه الوكيل إلى ميزة تناسب تقنياتك وواجهتك ومتطلباتك |
|
||||
|
||||
يوضح REA كيف وصل إلى نتائجه. وهو لا يدّعي استعادة الشيفرة المصدرية الأصلية أو استنساخ التطبيق تلقائيًا.
|
||||
|
||||
## لماذا REA؟
|
||||
|
||||
| | |
|
||||
| ------------------------ | ---------------------------------------------------------------------------- |
|
||||
| **مصمم للوكلاء** | اسأل عما يفعله التطبيق ودع الوكيل يفحصه بدلًا من التخمين. |
|
||||
| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو الوكيل. |
|
||||
| **إعداد موجّه** | اضبط وكيلك، واربط أداة تحليل موجودة، أو ثبّت Hopper بموافقتك. |
|
||||
| **من الفهم إلى الشيفرة** | افهم الميزة ثم ابنِ نسختك الخاصة في جلسة البرمجة نفسها. |
|
||||
| **محلي حسب التصميم** | يجري التحليل على مضيفك المحلي المدعوم، ولا يرفع REA التطبيق إلى خدمة مستضافة. |
|
||||
| **يحافظ على السياق** | استقصِ عدة تطبيقات دون بدء التحليل من جديد عند كل سؤال. |
|
||||
| | |
|
||||
| -------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| **مصمم للوكلاء** | اسأل عما يفعله تطبيق مترجم ودع الوكيل يجمع الأدلة بدلًا من التخمين. |
|
||||
| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو وكيل البرمجة. |
|
||||
| **يتولى التعقيد** | يدير REA إعداد الأدوات وفتح التطبيق واستمرار الاستقصاء والتنظيف بعد الانتهاء. |
|
||||
| **سير عمل كامل** | انتقل من النظرة الأولى إلى الشيفرة شبه المصدرية وعلاقات الاستدعاء والأنواع وأدلة التنفيذ. |
|
||||
| **محلي حسب التصميم** | يجري التحليل على جهاز Mac ولا يرفع REA الملف التنفيذي إلى خدمة تحليل مستضافة. |
|
||||
| **يحافظ على السياق** | استقصِ عدة ملفات تنفيذية من دون بدء التحليل من جديد عند كل سؤال. |
|
||||
|
||||
## البدء السريع
|
||||
|
||||
### تشغيل الإعداد (موصى به)
|
||||
|
||||
اربط REA بوكيلك:
|
||||
### باستخدام وكيل برمجة — موصى به
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
يعرض Setup أولاً قائمة متعددة الاختيار للوكلاء الذين تريد ربطهم. تكون تسجيلات REA الموجودة محددة افتراضياً، ولا يؤدي اكتشاف عميل جديد وحده إلى تحديده؛ ويمكنك اختيار عميل غير مهيأ. راجع المسارات والتغييرات المحددة قبل الموافقة. يثبت Setup سير عمل REA للوكلاء المحددين افتراضياً. Hopper خيار منفصل يتطلب موافقة مستقلة، ويمكن أيضاً حفظ مسارات تثبيت Ghidra الموجودة.
|
||||
اطلب من الوكيل إعداد REA. سيفحص جهاز Mac ويشرح ما يحتاج إلى تثبيته ويطلب الموافقة ويرشدك خلال رسائل النظام. أعد تشغيل الوكيل بعد الإعداد إذا طلب ذلك لتحميل مجموعة الأدوات الكاملة.
|
||||
|
||||
يعرض التغييرات قبل تطبيقها وينشئ نسخة احتياطية من الإعدادات الموجودة. راجع [دليل التثبيت والإعداد](docs/installation.md) للمتطلبات والخيارات الإضافية.
|
||||
|
||||
### الاستخدام مع وكيل
|
||||
|
||||
بعد الإعداد، أعد تشغيل الوكيل واشرح التطبيق أو الميزة التي تريد استقصاءها. يدعم REA Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin وOpenCode وAntigravity وGitHub Copilot CLI وCommand Code وVS Code. تكون تسجيلات REA الموجودة محددة افتراضياً، ويجب اختيار العملاء المكتشفين الآخرين يدوياً. يمكن للوكلاء الآخرين استخدام إعداد MCP أدناه.
|
||||
|
||||
يمكن تشغيل Hopper في الوضع التجريبي. إذا ظهرت رسالة عند التشغيل الأول، فاختر التجربة أو أدخل ترخيصًا موجودًا.
|
||||
|
||||
### مهارة مساعد البرمجة (اختياري)
|
||||
|
||||
أضف المهارة إلى مساعد البرمجة للحصول على سياق أغنى:
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
توفر المهارة سير عمل التحقيق في REA. نفّذ الإعداد أعلاه لربط REA بالوكيل وضبط أدوات التحليل. يثبّت الإعداد افتراضيًا مهارة مطابقة للإصدار؛ وهذا الأمر يثبّت نسخة المستودع.
|
||||
|
||||
لشجرة JavaScript/Electron المستخرجة أو ملف ASAR، لا يلزم إعداد MCP ولا محرك أصلي:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
استبدل المسار بهدفك (مثل `"D:/apps/example"` على Windows). يدعم سير العمل الثابت هذه المسارات، بينما يتطلب التحليل الأصلي محركه الخاص.
|
||||
|
||||
### تثبيت أمر rea
|
||||
|
||||
ثبت واجهة سطر الأوامر:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
يجب تثبيت Node.js وnpm مسبقًا. عند تشغيله من طرفية، يثبت البرنامج `rea` ويبدأ الإعداد.
|
||||
|
||||
يمكنك بدلًا من ذلك التثبيت باستخدام npm ثم تشغيل الإعداد:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### المتطلبات
|
||||
### قبل البدء
|
||||
|
||||
- macOS 12 أو أحدث
|
||||
- Ubuntu 24.04+ أو Fedora 41+ أو Arch Linux بنواة 64 بت أو CachyOS
|
||||
- Node.js 22.x (>=22.19) أو 24.x (>=24.11) أو 26+ وnpm
|
||||
- Node.js 24.18.x وnpm 11.16.x (شغّل `nvm use` لاختيار الإصدار المثبّت)
|
||||
|
||||
يتطلب تحليل الملفات التنفيذية الأصلية Hopper أو Ghidra. Hopper برنامج منفصل، وللوضع التجريبي قيود يحددها المورّد، لكن الترخيص المدفوع ليس إلزاميًا.
|
||||
لا تحتاج إلى تثبيت أدوات الهندسة العكسية يدويًا. يثبت Setup برنامج Homebrew و[Hopper](https://www.hopperapp.com/) عند الحاجة، ثم يهيئ وكلاء البرمجة المدعومين. Hopper برنامج منفصل يحتاج إلى ترخيص خاص به؛ يثبته Setup لكنه لا يوفر الترخيص.
|
||||
|
||||
يدعم Ghidra نظامي Linux x64 وmacOS x64/arm64. ثبت Ghidra 12.1.x وJDK الكامل بنواة 64 بت الذي يعلنه ذلك التثبيت (`application.java.min` حتى `application.java.max`) بصورة منفصلة، ثم اضبط REA لاستخدامهما. تتطلب إصدارات 12.1 الحالية JDK 21 أو أحدث ولا تحدد حدًا أعلى. جرى التحقق من الجسر مع Ghidra 12.1.4 وJDK 21. يحتاج macOS أيضًا إلى أداة فك الترجمة الأصلية المطابقة لمعمارية الجهاز.
|
||||
### 1. إعداد REA
|
||||
|
||||
يستطيع Setup التحقق من التثبيت وحفظ المسارات؛ ولا يثبت أو يحدّث Ghidra أو Java أو Node.js أو npm أو Homebrew.
|
||||
```bash
|
||||
npx -y rea-agents setup --yes
|
||||
```
|
||||
|
||||
يتضمن فرع main والحزمة npm 4.1.0 دعمًا تجريبيًا لـ Windows x64 Ghidra P0 لتطبيقات PE الأصلية x86-64 (غير المُدارة وغير DLL) على NTFS محلي، مع ضوابط Job Object وقوائم DACL الخاصة وقبول المسارات المجمّعة. تحقق من [حد الإصدار](docs/installation.md#released-package-and-main) قبل توقع ذلك من حزمة npm أقدم. راجع [دليل Windows Ghidra P0](docs/windows-ghidra-p0.md) للمتطلبات والنطاق الموثق.
|
||||
إذا طلب macOS أو برنامج التثبيت تأكيدًا، فأكمل الخطوة ثم شغّل الأمر نفسه مرة أخرى.
|
||||
|
||||
### تشخيص المشكلات
|
||||
### 2. إعادة تشغيل وكيل البرمجة
|
||||
|
||||
يشخّص `npx -y rea-agents@latest doctor` الجهاز والاعتماديات وأدوات التحليل وإعدادات الوكيل من دون تغييرها. أضف `--json` للحصول على تشخيص منظم.
|
||||
يهيئ Setup تلقائيًا نسخ Claude Desktop وCursor التي يعثر عليها. أعد تشغيل التطبيق كي يحمّل REA.
|
||||
|
||||
على Linux، يفضّل REA الملف التنفيذي `/opt/hopper/bin/Hopper`، وإذا لم يكن متاحًا يتحقق تلقائيًا من `~/.local/share/rea/hopper/bin/Hopper`. استخدم `HOPPER_LAUNCHER_PATH` للمسارات الأخرى. إذا كان الملف موجودًا لكن محرك التحليل لا يظهر، فشغّل `ldd /absolute/path/to/Hopper | grep 'not found'` على مسار Hopper الفعلي للتحقق من المكتبات الناقصة. راجع [دليل Hopper](docs/installation.md#hopper).
|
||||
### 3. اطلب من الوكيل
|
||||
|
||||
### التحديث والإزالة
|
||||
يمكنك ذكر التطبيق باسمه. سيعثر وكيل البرمجة عليه ويمرر ملف البرنامج الذي يحتاجه REA.
|
||||
|
||||
- يحدّث `rea update` تثبيت REA الحالي.
|
||||
- يزيل `rea uninstall` تسجيلات الوكلاء وملفات سير العمل التي يديرها REA، ويترك Hopper مثبتًا.
|
||||
- يزيل `rea uninstall --purge-data` أيضًا ذاكرة REA المؤقتة وحالته. استخدمه فقط إذا أردت حذف هذه البيانات.
|
||||
```text
|
||||
استخدم REA لإجراء هندسة عكسية لتطبيق Notes. اشرح طريقة عمل البحث واعرض الأدلة،
|
||||
ثم ابنِ ميزة مشابهة لمشروعي باستخدام SQLite.
|
||||
```
|
||||
|
||||
## حالة الدعم
|
||||
إذا واجهت مشكلة، فشغّل:
|
||||
|
||||
تُوصف قدرات المستودع الحالية ومتطلبات الأنظمة في [دليل الدعم الإنجليزي](README.md#current-status). قد يسبق فرع main [إصدار npm](docs/installation.md#released-package-and-main).
|
||||
|
||||
- يوفر Ghidra 25 عملية للقراءة فقط على Linux x64 وmacOS x64/arm64 وحدود Windows x64 P0 التجريبية. يدعم Linux/macOS أيضًا تعليقات الدوال الذرية على مستوى الجلسة. نظام Windows P0 للقراءة فقط، ولا يوفر Ghidra أي تحكم في الواجهة.
|
||||
- يتطلب الفحص الثابت لنظام Android تجهيز JADX/Java بشكل منفصل؛ ويغطي التحقق بالموفر الحقيقي نظام Linux. راجع [تحليل Android](docs/android-analysis.md).
|
||||
- تحدد طلبات المتصفح وElectron والعمليات هدفها وإجراءاتها ودورة حياتها مباشرة، وتعتمد على وصول المضيف الفعلي دون منح أذونات منفصلة من REA. ما زالت كتابة إعدادات Setup وتثبيت Hopper تتطلب الموافقة على خطتها الدقيقة.
|
||||
- يصف `rea capabilities` عمليات جلسة الملف التنفيذي لا كل أدوات التطبيقات ووقت التشغيل. راجع قائمة أدوات MCP الكاملة والدليل ذا الصلة.
|
||||
```bash
|
||||
npx -y rea-agents doctor
|
||||
```
|
||||
|
||||
## استقصاء كامل بطلب واحد
|
||||
|
||||
@@ -211,46 +145,31 @@ rea setup
|
||||
|
||||
افحص التحقق من الإدخال، والتشفير، والصلاحيات، والتخزين، ومسارات الأخطاء من دون رفع الملف التنفيذي إلى خدمة بعيدة.
|
||||
|
||||
## منصة أدوات التحقيق
|
||||
## منصة من 68 أداة
|
||||
|
||||
| عائلة الأدوات | العدد | الاستخدام |
|
||||
| ---------------------- | ----: | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| الفحص الأصلي | 41 | الدوال والشيفرة شبه المصدرية والتعليمات والسلاسل والرموز والاستدعاءات والمراجع والتعليقات وقراءة البايتات ومواضع الملفات |
|
||||
| سير عمل التحقيق | 14 | نظرات عامة على التطبيقات وملفات الدوال وواجهات API الأصلية والتوزيع وفك الترجمة الدفعي وتتبع الميزات ومسارات الاستدعاء ومخططاته واكتشاف Swift وObjective-C |
|
||||
| أدوات الملفات الثنائية | 10 | بيانات Mach-O والتوقيعات وplist والمعماريات وأسماء Swift ومراقبة الاستدعاءات عبر LLDB على macOS؛ تخطيط ELF وأدلة الحماية الثابتة وسجلات الأعطال على Linux |
|
||||
| مخطط المخرجات | 7 | جرد الدلائل والحزم وملفات Interface Builder المترجمة وكتالوجات موارد Apple وتحليل تحميل Mach-O dylib والاستخراج واستدلال واجهات EVM دون اتصال |
|
||||
| PE/CLI المُدار | 7 | هوية تجميعات .NET وبياناتها الوصفية وتعليمات CIL والاعتماديات الأصلية واستيراد إعادة البناء ومقارنة الإصدارات |
|
||||
| البرامج الثابتة | 2 | فحص مناطق البرامج الثابتة على Linux واستخراجها الصريح |
|
||||
| Android APK | 5 | بيانات الحزمة وmanifest والبحث عن الأصناف وقوائم الأعضاء وفك ترجمة الأساليب والمراجع الثابتة |
|
||||
| مراقبة المتصفح | 12 | بنية الصفحات وبيانات الشبكة والبرامج النصية وخرائط المصدر واكتشاف WebMCP والصور ومقارنة الالتقاط |
|
||||
| تحليل Electron | 5 | مراقبة العارضين وخريطة التطبيق الثابتة والمواءمة بين نتائج الثبات والتشغيل |
|
||||
| وقت تشغيل JavaScript | 2 | اكتشاف أهداف Node/Electron Inspector ومواضع البرامج وأحداث سياقات التنفيذ |
|
||||
| سير عمل التطبيقات | 13 | تصدير النصوص البرمجية الملتقطة، تتبع الميزات عبر الطبقات ومقارنة الإصدارات ومواءمة المصدر التاريخي ومقارنة أشكال القيم المعادة والتحقق من إعادة الإنشاء |
|
||||
| مساحة العمل والملاحظة | 21 | الجلسات وحزم الأدلة وسياق التنقل ومقارنة العمليات والمخرجات والدوال وتسجيل الأسئلة غير المحسومة |
|
||||
|
||||
## خطة العمل
|
||||
|
||||
يشمل العمل التالي توسيع التحقق من الأهداف الأصلية، ومقارنة إصدارات JavaScript و.NET، ومراقبة وقت التشغيل والتحقق من إعادة الإنشاء. يتيح Setup بالفعل اختيار تكامل الوكيل وتثبيت Hopper. تثبيت أدوات تحليل أخرى ما زال عملًا لاحقًا. راجع [خطة التثبيت](docs/roadmap.md) و[تقييم أدوات التحليل](docs/provider-evaluation.md).
|
||||
| عائلة الأدوات | العدد | أمثلة |
|
||||
| --------------------- | ----: | ----------------------------------------------------------------------------------- |
|
||||
| فحص الملفات التنفيذية | 33 | الدوال، والشيفرة شبه المصدرية، والتعليمات، والسلاسل، والأسماء، والمراجع، والتعليقات |
|
||||
| التحليل المركب | 10 | النظرة العامة، وفك الترجمة الدفعي، ومخططات الاستدعاء، والمراجع، واكتشاف الأنواع |
|
||||
| أدوات macOS الأصلية | 5 | بيانات Mach-O والتوقيعات وملفات plist والمعماريات وفك رموز Swift دون تشغيل Hopper |
|
||||
| رسم القطع الأثرية | 2 | جرد حتمي للدلائل وZIP/APK/IPA وASAR واستخراج معاملاتي محدد صراحة |
|
||||
| جلسة الملف التنفيذي | 18 | فتح الأهداف وحزم الأدلة ومقارنة العمليات والقطع والدوال وسجل المجهولات المتبقية |
|
||||
|
||||
## استخدام REA مع وكلاء برمجة آخرين
|
||||
|
||||
يدعم Setup Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin وOpenCode وAntigravity وGitHub Copilot CLI وCommand Code وVS Code. تكون تسجيلات REA الموجودة محددة افتراضياً، ويجب اختيار العملاء المكتشفين الآخرين يدوياً. يمكن لأي وكيل يدعم خوادم MCP المحلية الاتصال باستخدام الإعداد التالي:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
يهيئ Setup حاليًا Claude Desktop وCursor تلقائيًا. يمكن لأي وكيل برمجة يدعم خوادم MCP المحلية استخدام REA بالإعداد التالي:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@6.0.0", "mcp"]
|
||||
"args": ["-y", "rea-agents", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## كيف يعمل؟
|
||||
|
||||
```mermaid
|
||||
@@ -258,39 +177,40 @@ flowchart LR
|
||||
Human[المستخدم] --> Agent[وكيل البرمجة]
|
||||
Agent --> REA[REA]
|
||||
Human -->|CLI| REA
|
||||
REA --> Hopper[عمليات تحليل Hopper]
|
||||
REA --> Hopper[محرك التحليل]
|
||||
Hopper --> App[تطبيقك]
|
||||
REA --> Ghidra[تحليل Ghidra للقراءة فقط<br/>جرد + تحليل الدوال]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
يستخدم CLI وخادم MCP سير عمل التحليل نفسه. تحرر أوامر الطرفية جلسة الجسر الخاصة بها عند انتهائها، بينما يمكن لجلسة الوكيل الحفاظ على الاتصال أثناء الاستقصاء. إغلاق جلسة REA لا يغلق تطبيق Hopper الذي يستخدمه المستخدم.
|
||||
يستخدم CLI وخادم MCP محرك التحليل نفسه. تغلق أوامر الطرفية التطبيق عند انتهائها، بينما تبقيه جلسة الوكيل مفتوحًا أثناء الاستقصاء.
|
||||
|
||||
## أوامر CLI
|
||||
|
||||
سير عمل الوكيل أعلاه هو أسهل طريقة لاستخدام REA. للحصول على نظرة سريعة على تطبيق من الطرفية:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/path/to/right-evidence.json
|
||||
npx -y rea-agents analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
شغّل `npx -y rea-agents@latest --help` لمعرفة خيارات فك الترجمة المباشر والخيارات الأخرى.
|
||||
شغّل `npx -y rea-agents --help` لمعرفة خيارات فك الترجمة المباشر والخيارات الأخرى.
|
||||
|
||||
يمكن لـ REA فتح مجلد `.app` على Mac مباشرة. إذا لم يعثر الوكيل على التطبيق، فأخبره بمكان تثبيته.
|
||||
|
||||
## سلوك Hopper
|
||||
|
||||
يشغّل REA برنامج Hopper عند الحاجة. على macOS قد تظهر نافذته في المقدمة أو يحتاج أحد الحوارات إلى استجابة منك. لا يمكن ضمان بقاء النافذة في الخلفية.
|
||||
يستطيع REA تشغيل Hopper تلقائيًا؛ لا حاجة إلى افتراض أنه يعمل مسبقًا. لكنه تطبيق macOS رسومي، لذلك قد يطلب التركيز أو يعرض حوارًا عندما يفرض Hopper قرارًا تفاعليًا. لا يمكن لـ REA ضمان تشغيل غير مرئي تمامًا لأن هذه الواجهة يملكها Hopper وmacOS.
|
||||
|
||||
يحدد REA تنسيق الهدف ومعماريته لتجنب حوارات الاختيار المعتادة. عند إغلاق الجلسة، يوقف الجسر وينظف موارده الخاصة، لكنه لا يغلق تطبيق Hopper الذي تستخدمه.
|
||||
لتجنب مربع اختيار معمارية ملف شامل، حدد معطيات المحمل مسبقًا:
|
||||
|
||||
```bash
|
||||
export HOPPER_LOADER_ARGS_JSON='["-l", "Mach-O", "--aarch68"]'
|
||||
```
|
||||
|
||||
يدير REA دورة حياة Hopper ويحاول إغلاق العمليات التي بدأها عند انتهاء الأمر أو جلسة MCP.
|
||||
|
||||
## الأمان والخصوصية
|
||||
|
||||
- يبقى التحليل محليًا بين REA وموفر التحليل المختار عبر مقابس محلية خاصة، ومع IDA عبر تسجيل MCP المحلي المضبوط.
|
||||
- يبقى تحليل الملف التنفيذي محليًا بين REA وHopper.
|
||||
- لا يرفع REA الأهداف إلى خدمة مستضافة.
|
||||
- تعمل أدوات التحليل والأهداف المطلقة بصلاحيات المستخدم نفسه؛ وما زال التقاط واجهة النظام على macOS يعتمد على الوصول إلى تسهيلات الاستخدام وتسجيل الشاشة. ولا ينفّذ التحليل الثابت للـ JavaScript الوحدات المستخرجة.
|
||||
- تستخدم جلسات Ghidra مشروعًا مؤقتًا معزولًا ولا تفتح مشاريع Ghidra التي يملكها المستخدم أو تعدّلها.
|
||||
- راجع الملفات التنفيذية غير الموثوقة واعزلها كما تفعل مع أي مدخل أصلي قد يكون ضارًا.
|
||||
- أبلغ عن الثغرات وفق [سياسة الأمان](SECURITY.md)، وليس عبر قضية عامة.
|
||||
|
||||
@@ -313,7 +233,7 @@ npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/
|
||||
<details>
|
||||
<summary><strong>هل يعمل REA على Linux أو Windows؟</strong></summary>
|
||||
|
||||
يدعم REA نظام macOS 12+ وUbuntu 24.04+ وFedora 41+ وArch Linux بنواة 64 بت وCachyOS. يعمل تحليل Ghidra للقراءة فقط على Linux x64 وmacOS x64/arm64 مع Ghidra 12.1.x وJDK الكامل الذي يعلنه التثبيت (JDK 21 أو أحدث دون حد أعلى في إصدارات 12.1 الحالية)، إضافة إلى حد Windows x64 P0 التجريبي لتطبيقات PE الأصلية x86-64 على NTFS محلي؛ راجع [دليل Windows Ghidra P0](docs/windows-ghidra-p0.md) للنطاق الموثق.
|
||||
ليس حاليًا. يعتمد المحول الحالي على تطبيق Hopper لنظام macOS وواجهة Python الخاصة به.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -336,20 +256,6 @@ npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/
|
||||
- [Hopper Disassembler](https://www.hopperapp.com/)
|
||||
- [سياسة الأمان](SECURITY.md)
|
||||
|
||||
## تاريخ النجوم
|
||||
|
||||
🎉 **وصلنا إلى 20,000 نجمة على GitHub، شكرًا لكم!**
|
||||
|
||||
شكرًا لكل من يستخدم REA ويبلّغ عن الأخطاء ويختبر الإصدارات ويساهم في إصلاحها.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="تاريخ نجوم REA على GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## الترخيص
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
+90
-238
@@ -4,62 +4,44 @@
|
||||
|
||||
# REA:あらゆるものをリバースエンジニアリング
|
||||
|
||||
### バイナリ、アプリ、実行時の動作をひとつの MCP でリバースエンジニアリング。
|
||||
### コーディングエージェントがあらゆるものをリバースエンジニアリングするための、ひとつの CLI / MCP サーバー
|
||||
|
||||
**気に入った機能を見つけたら、その仕組みをバイナリのレベルまで理解する。**
|
||||
**気になる機能を見つけ、仕組みを理解し、自分の形で実装する。**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#調査ツールカタログ)
|
||||
[](https://nodejs.org/)
|
||||
[](#68-ツールのワークベンチ)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[ウェブサイト(英語)](https://morluto.github.io/rea/) · [ガイド](https://morluto.github.io/rea/guides/) · [DX-Ball の事例](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[クイックスタート](#クイックスタート) · [現在の対応状況](#現在の対応状況) · [バイナリから動作へ](#バイナリから動作へ) · [調査ツールカタログ](#調査ツールカタログ) · [ロードマップ](#ロードマップ) · [仕組み](#仕組み)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>リバースエンジニアリングのコミュニティに参加</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 質問と回答 · 成果の共有</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
[クイックスタート](#クイックスタート) · [バイナリから動作へ](#バイナリから動作へ) · [68 ツール](#68-ツールのワークベンチ) · [仕組み](#仕組み) · [FAQ](#faq)
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="Hopper 内で解析ブリッジを起動し、ネイティブバイナリを調べる REA" width="1200" />
|
||||
<code>npx skills add morluto/rea</code>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
自分のプロダクトにも欲しい機能を他のアプリで見つけたら、REA を使ってエージェントに調査を依頼できます。エージェントはソースコードがなくてもアプリを調べ、機能の仕組みを説明して根拠を示し、あなたのプロジェクト向けの実装まで進められます。
|
||||
アプリの気になる機能を自分のプロダクトにも取り入れたいと思ったことはありませんか。ソースコードがなくても、そのアプリをコーディングエージェントに渡せます。REA を使えば、エージェントが機能を調査して仕組みを理解し、あなたの技術スタック、デザイン、要件に合わせた形で実装できます。
|
||||
|
||||
REA は、ネイティブバイナリ、JavaScript/Electron アプリ、.NET アセンブリ、Web サイトを調べるためのツールをエージェントに提供します。同じツールはターミナルからも使えます。解析はローカルで実行され、結果には各結論の根拠と制限が含まれます。
|
||||
|
||||
セットアップは REA をエージェントに登録し、対応するワークフロー手順もインストールします。ネイティブ解析には既存の Hopper または Ghidra を使えます。承認すれば、セットアップで Hopper をインストールすることもできます。静的な JavaScript 解析にはどちらのエンジンも不要です。
|
||||
REA はこの流れをひとつの CLI / MCP サーバーで実現します。エージェントはコンパイル済みのアプリを調べ、機能の流れを追い、分かったことを通常のコーディング作業に活用できます。複雑なリバースエンジニアリングツールは REA がひとつのインターフェースの裏側で扱います。
|
||||
|
||||
## エージェントに頼むだけ
|
||||
|
||||
[セットアップ](#クイックスタート)後にエージェントを再起動し、次のように依頼します:
|
||||
|
||||
```text
|
||||
メモアプリの検索機能を調べて根拠を示し、私のプロジェクト向けに同様の機能を実装してください。
|
||||
```bash
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
「メモアプリ」を調べたいアプリに置き換えてください。まずは概要の調査を依頼することもできます。
|
||||
次のように依頼します:
|
||||
|
||||
```text
|
||||
REA をセットアップしてメモアプリをリバースエンジニアリングしてください。
|
||||
検索の仕組みと判断の理由を示し、私のプロジェクト向けに同様の機能を実装してください。
|
||||
```
|
||||
|
||||
メモアプリは例にすぎません。調べたいアプリを指定するか、まず概要から始めるよう依頼できます。
|
||||
|
||||
## バイナリから動作へ
|
||||
|
||||
@@ -67,144 +49,62 @@ REA は、ネイティブバイナリ、JavaScript/Electron アプリ、.NET ア
|
||||
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| ネイティブアプリや実行ファイルから、プロシージャ、疑似コード、アセンブリ、文字列、シンボル、セグメント、メタデータを復元します。 | 呼び出し元、呼び出し先、クロスリファレンス、コールグラフをたどり、機能やアルゴリズムの実際の動作を説明できる状態にします。 | エージェントが得た知見を、あなたの技術スタック、画面、要件に合うプロダクト機能へ変えます。 |
|
||||
|
||||
REA は結論に至った根拠を示します。元のソースコードを復元できる、あるいはアプリ全体を自動的に複製できるとは主張しません。
|
||||
REA は調査をバイナリ上の根拠に結び付けます。元のソースコードを復元したり、アプリ全体を自動複製したりするとは主張しません。
|
||||
|
||||
## REA を選ぶ理由
|
||||
|
||||
| | |
|
||||
| -------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| **エージェント向け** | アプリの動作を質問すると、エージェントが推測ではなく実際に調べて答えます。 |
|
||||
| **CLI と MCP** | 同じリバースエンジニアリング機能を、ターミナルからもエージェントからも使えます。 |
|
||||
| **ガイド付きセットアップ** | エージェントの設定、既存の解析ツールへの接続、承認したうえでの Hopper のインストールに対応します。 |
|
||||
| **洞察からコードへ** | 機能を理解したら、同じコーディングセッションで自分のプロダクト向けに実装できます。 |
|
||||
| **ローカルで解析** | 解析は対応するローカルホストで実行されます。REA がアプリをホスト型の解析サービスへ送ることはありません。 |
|
||||
| **コンテキストを維持** | 質問のたびに解析をやり直すことなく、複数のアプリを続けて調査できます。 |
|
||||
| | |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **エージェント向け** | コンパイル済みアプリについて質問し、推測ではなく根拠を集めさせることができます。 |
|
||||
| **CLI と MCP** | ターミナルとコーディングエージェントから同じリバースエンジニアリング機能を使えます。 |
|
||||
| **複雑さを処理** | ツール設定、アプリの読み込み、調査の維持、終了後のクリーンアップを REA が担います。 |
|
||||
| **一連の調査に対応** | 最初の概要から疑似コード、呼び出し関係、型、実装の手掛かりまで掘り下げられます。 |
|
||||
| **ローカルで解析** | 解析は Mac 上で実行され、REA がバイナリをホスト型解析サービスへ送ることはありません。 |
|
||||
| **コンテキストを維持** | 質問ごとに解析を最初からやり直さず、複数のバイナリを続けて調査できます。 |
|
||||
|
||||
## クイックスタート
|
||||
|
||||
### セットアップを実行(推奨)
|
||||
|
||||
REA をエージェントで使えるように設定します:
|
||||
### コーディングエージェントから(推奨)
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
セットアップでは、まず REA を使うエージェントを選び(複数選択可)、具体的なパスと変更内容を確認してから承認します。既存の REA 登録は最初から選択されています。新しく検出されたエージェントは選択肢に表示されますが、検出されただけでは選択されません。選択したエージェントには、MCP 接続と REA のガイド付きワークフローが追加されます。Hopper のインストールは別の任意項目で、個別に承認が必要です。既存の Ghidra のインストール先を登録することもできます。
|
||||
エージェントに REA のセットアップを依頼してください。Mac を確認し、必要なインストールを説明して承認を求め、システムプロンプトを案内します。完全なツールを読み込むため再起動を求められた場合は、セットアップ後に再起動してください。
|
||||
|
||||
セットアップは変更を適用する前に内容を表示し、既存の設定をバックアップします。要件とセットアップのオプションは[インストールガイド](docs/installation.md)を参照してください。
|
||||
|
||||
### エージェントで使う(推奨)
|
||||
|
||||
セットアップ後にエージェントを再起動し、理解したいアプリや機能を[説明してください](#エージェントに頼むだけ)。Hopper はデモモードでも動作します。初回起動の画面が表示されたら、デモを選ぶか既存のライセンスを入力してください。
|
||||
|
||||
REA は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code、VS Code に対応しています。セットアップでは既存の REA 登録が最初から選択され、それ以外の検出済みエージェントは選択するまで対象になりません。その他のエージェントは[手動の MCP 設定](#他のコーディングエージェントで使う)を使えます。
|
||||
|
||||
### AI コーディングアシスタント向けスキル(任意)
|
||||
|
||||
AI コーディングアシスタントにスキルを追加すると、より豊富なコンテキストを利用できます:
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
このスキルは REA の調査ワークフローを提供します。REA をエージェントに接続して解析ツールを設定するには、上記のセットアップを実行してください。セットアップは既定でバージョンの一致したスキルをインストールします。このコマンドはリポジトリ版のスキルをインストールします。
|
||||
|
||||
### ターミナルで最初の結果を得る
|
||||
|
||||
展開済みの JavaScript/Electron アプリのディレクトリまたは ASAR に対して、次を実行します:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
パスは調査対象に置き換えてください(Windows では `"D:/apps/example"` など)。MCP の設定、Hopper、Ghidra を必要とせず、アプリを実行することもなく、Evidence、復元したグラフ、制限、未解決事項をその場で返します。ネイティブアプリの場合は、先にエンジンを設定してから、そのアプリのパスを指定して `analyze` を使います。`doctor` は問題を診断したいときに使うもので、解析のたびに実行する必要はありません。
|
||||
|
||||
### 作業に合わせて準備状況を確認する
|
||||
|
||||
オプションなしの `rea doctor` は連携全体の監査です。検出したすべてのエージェント登録、インストール済みのスキル、すべての任意の解析エンジンを確認するため、今の作業には支障がなくても `healthy: false` を報告することがあります。作業に合わせて確認範囲を指定してください:
|
||||
|
||||
| 作業 | 準備状況の確認 |
|
||||
| ------------------------------------------ | ---------------------------------------------------------------------------------------------- |
|
||||
| 静的な JavaScript/Electron 解析 | 不要です。`analyze-javascript-application` を直接実行します。 |
|
||||
| 特定の解析エンジンのトラブルシューティング | `rea doctor --provider ghidra --json`(または `hopper`、`ida`) |
|
||||
| 特定のエージェントの MCP 登録を確認 | `rea doctor --client codex --json`([クライアント ID](docs/installation.md#supported-agents)) |
|
||||
| インストール済みのスキルを確認 | `rea doctor --skill --json` |
|
||||
|
||||
範囲を指定したレポートでは、`healthy` と終了ステータスを決めるのは `scope_checks` だけです。それ以外の項目は `informational_checks` に表示され、今の作業のために修正する必要はありません。たとえば、使っていないエンジンが見つからなくても対処は不要です。
|
||||
|
||||
ネイティブ対象に対応するエンジンが複数インストールされている場合、REA は自動ではどれも選びません。対象を開くと `code: "capability_unavailable"` と `details.selection_reason: "ambiguous"` で失敗し、`details.candidate_ids` に候補が示されます。CLI の `--provider` か `open_binary` の `provider_id` で一度選ぶか、常用する設定として `REA_ANALYSIS_PROVIDER` を指定してください。詳しくは[英語版の解説](README.md#choosing-a-deep-analysis-provider)を参照してください。
|
||||
|
||||
### rea コマンドをインストール
|
||||
|
||||
コマンドラインツールをインストールします:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
Node.js と npm を先にインストールしておいてください。ターミナルで実行すると、インストーラーは `rea` を追加してセットアップを開始します。
|
||||
|
||||
npm でインストールしてから、セットアップを実行することもできます:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 要件
|
||||
|
||||
静的な JavaScript 解析に必要なのは Node.js と npm だけです。ホストと外部ツールの前提条件は使うワークフローによって異なり、ネイティブ解析プロバイダーの対応プラットフォームは各ガイドで説明しています。
|
||||
### 始める前に
|
||||
|
||||
- macOS 12 以降
|
||||
- Ubuntu 24.04+、Fedora 41+、64 ビット Arch Linux、または CachyOS
|
||||
- Node.js 22.x (>=22.19)、24.x (>=24.11)、または 26+
|
||||
- npm(特定のバージョンは不要で、REA が npm をインストールすることもありません)
|
||||
- Node.js 24.18.x と npm 11.16.x(`nvm use` で固定バージョンを選択)
|
||||
|
||||
ネイティブバイナリの詳細な解析には Hopper、Ghidra、IDA Pro のいずれかが必要です。Hopper は独自のライセンスを持つ別製品で、デモ版でもベンダーが定める制限の範囲で解析できます。Ghidra と IDA は、利用者が用意したものを使います。
|
||||
|
||||
Ghidra は Linux x64 と macOS x64/arm64 に対応しています。Ghidra 12.1.x と、そのインストールが宣言する 64 ビットの完全な JDK(`application.java.min` から `application.java.max` まで)を別途インストールし、REA で使うように設定してください。現行の 12.1 リリースは JDK 21 以降を必要とし、上限はありません。ブリッジは Ghidra 12.1.4 と JDK 21 で検証しています。macOS では、ホストのアーキテクチャに合うネイティブデコンパイラーも必要です。
|
||||
リバースエンジニアリングツールを手動でインストールする必要はありません。Setup は必要に応じて Homebrew と [Hopper](https://www.hopperapp.com/) をインストールし、対応するコーディングエージェントを設定します。Hopper は別製品で、ライセンスは別途必要です。Setup はインストールしますが、ライセンスは提供しません。
|
||||
|
||||
```bash
|
||||
export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.4_PUBLIC
|
||||
export JAVA_HOME=/absolute/path/to/jdk-21 # java と javac が PATH から解決できる場合は省略可
|
||||
rea doctor --provider ghidra --json
|
||||
rea setup
|
||||
# 1. REA をセットアップ
|
||||
npx -y rea-agents setup --yes
|
||||
```
|
||||
|
||||
セットアップはこれらのインストールを確認し、承認後にパスを保存します。Ghidra、Java、Node.js、npm、Homebrew のインストールや更新は行いません。
|
||||
macOS やインストーラーから確認を求められた場合は、その操作を完了してから同じコマンドをもう一度実行してください。
|
||||
|
||||
IDA Pro は、すでに動作している [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) の MCP 登録を再利用します。開いている GUI の対象を読み取り専用で解析するか(`attached`、既定)、データベースのスーパーバイザーで指定したバイナリをヘッドレスで開いて解析します(`headless`)。REA が attached の GUI データベースを保存したり閉じたりすることはありません。最初の実環境での検証は、Windows の GUI と Windows x64 のヘッドレス IDA 9.3 が対象です。設定方法と対応範囲は [IDA プロバイダーガイド](docs/ida-provider.md)を参照してください。
|
||||
### 2. コーディングエージェントを再起動
|
||||
|
||||
Setup は検出した Claude Desktop と Cursor を自動設定します。REA を読み込むため、アプリを再起動してください。
|
||||
|
||||
### 3. エージェントに依頼
|
||||
|
||||
アプリは名前で指定できます。コーディングエージェントがアプリを探し、REA に必要なプログラムファイルを渡します。
|
||||
|
||||
```text
|
||||
REA でメモアプリをリバースエンジニアリングしてください。検索機能の仕組みと
|
||||
根拠を示し、SQLite を使って私のプロジェクト向けに同様の機能を実装してください。
|
||||
```
|
||||
|
||||
問題がある場合は次を実行します:
|
||||
|
||||
```bash
|
||||
export REA_IDA_MCP_CONFIG=/absolute/path/to/ida-mcp.json
|
||||
rea function /absolute/path/to/program main --provider ida --json
|
||||
npx -y rea-agents doctor
|
||||
```
|
||||
|
||||
リポジトリの main と npm 4.1.0 には、ローカル NTFS 上のネイティブ x86-64 PE アプリケーション(マネージドコードと DLL を除く)を対象とする、実験的な Windows x64 Ghidra P0 対応が含まれています。Job Object、プライベート DACL、パスの受け入れ検査の各制御も同梱されています。古い npm パッケージでこの機能を使えるか確認するには[リリース境界](docs/installation.md#released-package-and-main)を、前提条件と検証済みの範囲は [Windows Ghidra P0](docs/windows-ghidra-p0.md) を参照してください。
|
||||
|
||||
### トラブルシューティング
|
||||
|
||||
`npx -y rea-agents@latest doctor` は、ホスト、依存関係、解析ツール、エージェント設定を変更せずに確認します。構造化された診断結果が必要な場合は `--json` を追加してください。
|
||||
|
||||
Linux では実行可能な `/opt/hopper/bin/Hopper` を優先し、利用できない場合は `~/.local/share/rea/hopper/bin/Hopper` を自動的に確認します。それ以外の場所にインストールした場合は `HOPPER_LAUNCHER_PATH` を設定してください。ファイルがあるのに解析エンジンが見つからないと報告される場合は、実際の Hopper のパスに対して `ldd /absolute/path/to/Hopper | grep 'not found'` を実行し、不足しているライブラリを確認してください。詳しくは [Hopper ガイド](docs/installation.md#hopper)を参照してください。
|
||||
|
||||
### 更新とアンインストール
|
||||
|
||||
- `rea update` は現在の REA インストールを更新します。
|
||||
- `rea uninstall` は REA が管理するエージェント登録とスキルだけを削除します。Hopper、Node.js、Evidence ファイル、キャプチャ、無関係なスキル、他の MCP サーバーは残ります。
|
||||
- `rea uninstall --purge-data` は `~/.rea/cache` と `~/.rea/state` も削除します。それらを消したい場合にだけ使ってください。
|
||||
|
||||
## 現在の対応状況
|
||||
|
||||
リポジトリの現在の機能とプラットフォーム要件は[英語の対応状況ガイド](README.md#current-status)で説明しています。main は [npm リリース](docs/installation.md#released-package-and-main)より先行する場合があります。
|
||||
|
||||
- Ghidra は Linux x64、macOS x64/arm64、実験的な Windows x64 P0 境界で 25 種類の読み取り専用操作を提供します。Linux と macOS では、セッション内での関数名とエントリーコメントのアトミックな編集にも対応します。Windows P0 には変更の権限がなく、Ghidra には REA から操作できる GUI 機能はありません。
|
||||
- IDA アダプターは、ドキュメントに記載された読み取り専用の関数・文字列操作を提供します。
|
||||
- 静的な Android APK の検査は Linux と macOS に対応し、別途用意したヘッドレス JADX と完全な JDK が必要です。現在のメタデータブリッジは macOS arm64 で検証しています。詳しくは [Android 解析](docs/android-analysis.md)を参照してください。
|
||||
- ブラウザー、Electron、プロセスの各リクエストは対象、操作、ライフサイクルを直接指定し、ホストで実際に許可されたアクセスの範囲で動作します。REA 側で別途権限を付与する必要はありません。セットアップによる設定の書き込みと Hopper のインストールには、引き続き具体的な計画の承認が必要です。
|
||||
- `rea capabilities` と `rea providers` はバイナリセッションのプロバイダーと補助操作を説明するもので、ブラウザー、Android、アプリのすべてのワークフローの一覧ではありません。MCP の機能全体は、接続中の MCP ツール一覧と `binary_session` が示すツールの利用可否で確認してください。
|
||||
|
||||
## ひとつのプロンプトで調査を完結
|
||||
|
||||
```text
|
||||
@@ -216,97 +116,71 @@ TypeScript と SQLite を使って私のプロジェクト向けに実装して
|
||||
| ---: | ------------------------------ | ---------------------------------------------------------------- |
|
||||
| 1 | バイナリを開いて識別 | `open_binary`, `binary_overview` |
|
||||
| 2 | オフライン検索の手掛かりを探す | `search_strings`, `search_procedures`, `list_names` |
|
||||
| 3 | 手掛かりを実行コードに結び付け | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | 関連する制御フローを復元 | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 3 | 手掛かりと実行コードを接続 | `find_xrefs_to_name`, `xrefs`, `procedure_callers` |
|
||||
| 4 | 制御フローを復元 | `get_call_graph`, `procedure_callees`, `procedure_info` |
|
||||
| 5 | 関連する処理を逆コンパイル | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | プロジェクトに機能を実装 | 技術スタック、プロダクト、要件に合わせたコード |
|
||||
| 6 | プロジェクトに機能を実装する | 技術スタック、プロダクト、要件に合わせたコード |
|
||||
|
||||
手順 1〜5 のアプリ解析は REA が担当します。手順 6 は、アプリについて得た知見をもとに、エージェントが通常の編集ツールとテストツールで行います。
|
||||
REA は手順 1〜5 のバイナリ解析を処理し、手順 6 はエージェントの通常の編集・テストツールが行います。
|
||||
|
||||
## エージェントにできること
|
||||
|
||||
- 気に入った機能を調査し、自分のプロダクトに合わせた形で実装する。
|
||||
- ソースコードが手に入らない機能の仕組みを説明する。
|
||||
- アプリの認証、保存、更新、ネットワーク処理の流れを復元する。
|
||||
- 文書化されていない形式やインターフェースについて、文書化に必要な構造を復元する。
|
||||
- 文字列やシンボルから、疑わしい動作を実装しているコードまでたどる。
|
||||
- 復元した動作を、プロダクト機能、テスト、移行メモ、移植版、相互運用できる代替実装に変える。
|
||||
- マングルされたシンボルをひとつずつ手作業で読み解かなくても、Swift と Objective-C のメタデータを解析する。
|
||||
- Hopper に名前、コメント、ブックマークを残し、人とエージェントの解析を互いに活かす。
|
||||
- ソースコードがない機能の仕組みを説明する。
|
||||
- アプリの認証、保存、更新、ネットワークフローを復元する。
|
||||
- 非公開の形式やインターフェースを文書化できる構造を回収する。
|
||||
- 文字列やシンボルから疑わしい動作の実装コードまで追跡する。
|
||||
- 同じセッションで 2 バージョンを切り替え、実装経路を比較する。
|
||||
- 気になる機能を調査し、自分のプロダクトに合わせた形で実装する。
|
||||
- 復元した動作をプロダクト機能、テスト、移行ノート、移植、相互運用できる代替実装へ変換する。
|
||||
- Swift / Objective-C のメタデータを解析する。
|
||||
- Hopper に名前、コメント、ブックマークを残し、人間とエージェントの調査を共有する。
|
||||
|
||||
## 調査ツールカタログ
|
||||
## 68 ツールのワークベンチ
|
||||
|
||||
| ツール分類 | 数 | 用途 |
|
||||
| -------------------- | --: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ネイティブ検査 | 41 | 関数、疑似コード、アセンブリ、文字列、シンボル、呼び出し、参照、注釈、バイト読み取り、ファイルオフセット |
|
||||
| 調査ワークフロー | 14 | アプリ概要、関数ドシエ、ネイティブ API とディスパッチ、一括逆コンパイル、機能トレース、コールパス、コールグラフ、Swift と Objective-C の検出 |
|
||||
| ネイティブバイナリ | 10 | macOS の Mach-O メタデータ、署名、plist、アーキテクチャ、Swift シンボル、LLDB による呼び出し観測;Linux の ELF レイアウト、静的な保護機構の証拠と記録済みクラッシュ |
|
||||
| 成果物グラフ | 7 | ディレクトリとパッケージの目録、コンパイル済み Interface Builder ファイル、Apple アセットカタログ、Mach-O dylib のロード解決、抽出、オフライン EVM インターフェース推定 |
|
||||
| マネージド PE/CLI | 7 | .NET の識別情報、メタデータ、CIL 命令、ネイティブ依存関係、再構築結果のインポート、ビルド比較 |
|
||||
| ファームウェア | 2 | Linux ファームウェア領域の検査と明示的な展開 |
|
||||
| Android APK | 5 | パッケージと manifest の宣言、クラス検索、メンバー一覧、メソッドの逆コンパイル、静的な被参照 |
|
||||
| ブラウザー観察 | 12 | ページ構成、ネットワークメタデータ、スクリプト、ソースマップ、WebMCP の検出、スクリーンショット、キャプチャ比較 |
|
||||
| Electron 解析 | 5 | レンダラーの観察、静的なアプリのマッピング、静的結果と実行時結果の照合 |
|
||||
| JavaScript 実行時 | 2 | Node/Electron Inspector の対象検出、スクリプトの位置、実行コンテキストのイベント |
|
||||
| アプリワークフロー | 13 | 取得した Web スクリプトのエクスポート、Android/Apple の目録の投影、層をまたぐ機能トレース、ビルド比較、過去のソースとの対応付け、静的な戻り値形状の比較、再構築の検証 |
|
||||
| ワークスペースと観察 | 21 | セッション、Evidence バンドル、ナビゲーションの文脈、プロセス・成果物・関数の比較、未解決事項の記録 |
|
||||
|
||||
## ロードマップ
|
||||
|
||||
[現在の対応状況](#現在の対応状況)は提供済みの機能です。今後の作業は次のとおりです。
|
||||
|
||||
- **現在:** ツール、プロバイダー、セットアップの選択肢、バージョンの変更に合わせてドキュメントを正確に保ち、Hopper と Ghidra で検証するネイティブバイナリのアーキテクチャと間接呼び出しの範囲を広げます。
|
||||
- **次:** 機能トレースに静的抽出と実行時の観察を追加し、難読化された .NET アセンブリの比較を改善し、プロセス、プロトコル、ファイルシステム、再接続、バージョン比較の範囲を広げます。
|
||||
- **その後:** ブラウザーと Electron のシナリオ操作を増やし、LLDB や Frida などによるネイティブアプリの実行時観察と、他の解析ツールや対象の評価を進めます。
|
||||
|
||||
セットアップでは、すでにエージェント連携と Hopper のインストールを選べます。その他の解析ツールのインストールへの対応は今後の作業です。[インストールのロードマップ](docs/roadmap.md)と[解析ツールの評価](docs/provider-evaluation.md)を参照してください。
|
||||
| ツール群 | 数 | 例 |
|
||||
| ---------------------- | --: | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| バイナリ調査 | 33 | プロシージャ、疑似コード、アセンブリ、文字列、名前、セグメント、callers、callees、xrefs、注釈 |
|
||||
| 合成解析 | 10 | `binary_overview`, `analyze_function`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift / ObjC 検出 |
|
||||
| macOS ネイティブ | 5 | Mach-O メタデータ、署名、plist、アーキテクチャ、Swift デマングル。Hopper 起動不要 |
|
||||
| アーティファクトグラフ | 2 | ディレクトリ、ZIP/APK/IPA、ASAR の決定的一覧と明示選択されたトランザクション抽出 |
|
||||
| バイナリセッション | 18 | `open_binary`、`binary_session`、証拠バンドル、プロセス/成果物/関数比較、残余未知レジストリ |
|
||||
|
||||
## 他のコーディングエージェントで使う
|
||||
|
||||
セットアップは Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code、VS Code に対応しています。既存の REA 登録は最初から選択され、それ以外の検出済みエージェントは選択するまで対象になりません。ローカル MCP サーバーに対応するエージェントであれば、次の設定でも接続できます。
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
Setup は現在 Claude Desktop と Cursor を自動設定します。ローカル MCP サーバーに対応するコーディングエージェントなら、次の設定で REA を利用できます。
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@6.0.0", "mcp"]
|
||||
"args": ["-y", "rea-agents", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
常用する登録では、パッケージのバージョンを 1 つに固定してください。`rea setup` はその固定バージョンを維持し、同梱のスキルも同時に更新します。
|
||||
|
||||
## 仕組み
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["コーディングエージェント"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal["ターミナル"] --> REA
|
||||
REA --> Hopper["Hopper プロバイダー"]
|
||||
REA --> Ghidra["Ghidra プロバイダー<br/>インベントリ + 関数解析 + 注釈"]
|
||||
REA --> Ida["IDA MCP プロバイダー<br/>attached GUI または headless データベース"]
|
||||
REA --> Hopper["解析エンジン"]
|
||||
Hopper --> App["あなたのアプリ"]
|
||||
Ghidra --> App
|
||||
Ida --> App
|
||||
```
|
||||
|
||||
CLI と MCP サーバーは、同じアプリケーションワークフローと Evidence の契約を使います。ターミナルのコマンドは完了すると自身のブリッジセッションを解放し、エージェントのセッションは調査中の対象と Evidence を保持できます。REA のセッションを閉じても、利用中の Hopper アプリは終了しません。
|
||||
CLI と MCP サーバーは同じ解析エンジンを使います。ターミナルのコマンドは終了時にアプリを閉じ、エージェントのセッションは調査中アプリを開いたままにします。
|
||||
|
||||
## CLI
|
||||
|
||||
上のエージェントワークフローが、REA を使う最も簡単な方法です。ターミナルからアプリの概要を一度だけ調べる場合は、次を実行します。
|
||||
上のエージェントワークフローが、REA を使う最も簡単な方法です。ターミナルから一度だけアプリの概要を調べる場合は、次を実行します。
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
直接の逆コンパイルやその他のオプションは、`npx -y rea-agents@latest --help` で確認できます。
|
||||
直接デコンパイルする方法やその他のオプションは、`npx -y rea-agents --help` で確認できます。
|
||||
|
||||
グローバルな `rea` コマンドとしてもインストールできます。
|
||||
|
||||
@@ -316,49 +190,41 @@ rea --help
|
||||
rea mcp
|
||||
```
|
||||
|
||||
REA は Mac の `.app` フォルダーを直接開けます。エージェントがアプリを名前で見つけられない場合は、インストール場所を伝えてください。
|
||||
REA は Mac の `.app` フォルダーを直接開けます。エージェントがアプリを見つけられない場合は、インストール場所を伝えてください。
|
||||
|
||||
## Hopper アプリの動作
|
||||
|
||||
REA は操作に必要になった時点で Hopper を起動します。Hopper のランチャーは内部でアプリをアクティブにするため、REA がバックグラウンドでの起動を要求しても、macOS では Hopper のウィンドウやダイアログが前面に出る場合があります。デモやライセンスの画面では操作が必要になることがあります。
|
||||
REA は必要なときに Hopper を起動します。Hopper のランチャーは内部でアプリをアクティブ化するため、ターゲットを開くと Hopper が前面に出る場合があります。REA はバックグラウンド起動を要求しますが、常に背面に留まる保証はありません。
|
||||
|
||||
Hopper は解析リクエストを一度に 1 つずつ処理します。待機をキャンセルしても、Hopper 内ですでに実行中の処理は止まりません。処理中かどうかはセッションが報告します。セッションを閉じると REA のブリッジと一時ソケットディレクトリを削除しますが、利用中の Hopper アプリは終了しません。後片付けを確認できなかった場合、`close_binary` は `cleanup_incomplete` と対象のリソースを報告します。
|
||||
明示的な形式・アーキテクチャ引数により一般的な FAT / ARM 選択ダイアログを避けますが、別の Hopper / macOS ダイアログは人の操作を必要とする場合があります。セッションを閉じるとブリッジとソケットを削除しますが、ユーザーが利用中の Hopper は終了しません。
|
||||
|
||||
## セキュリティモデル
|
||||
|
||||
解析はローカルで実行されます。REA は Hopper や Ghidra とは認証付きのプライベートなローカルソケットで、IDA とは設定済みのローカル MCP 登録を通じて通信します。Ghidra セッションは隔離された一時プロジェクトを使い、ユーザーの Ghidra プロジェクトを開いたり変更したりしません。
|
||||
|
||||
これはサンドボックスではありません。解析ツールと起動した対象は現在のユーザー権限で動作し、同じ OS ユーザー権限で動く悪意のあるプロセスは防げません。Windows Ghidra P0 では、ネイティブの Job Object、保護されたプライベート DACL、ハンドルベースの受け入れ検査を自動的に使います。脆弱性は [SECURITY.md](SECURITY.md) に記載された非公開の手順で報告してください。
|
||||
各セッションはランダムな capability token と現在のユーザーだけが使える Unix ソケットを使用します。これはサンドボックスではなく、同じユーザー権限で動作する悪意あるプロセスを防御しません。信頼できないバイナリの解析は、現在の macOS ユーザー権限で Hopper に委譲されます。脆弱性は [SECURITY.md](SECURITY.md) の非公開手順で報告してください。
|
||||
|
||||
## FAQ
|
||||
|
||||
<details><summary><strong>Hopper を先に起動しておく必要はありますか?</strong></summary>
|
||||
<details><summary><strong>Hopper を先に起動する必要がありますか?</strong></summary>
|
||||
|
||||
いいえ。REA は必要になった時点で Hopper を起動します。すでに起動している Hopper でも使えますが、既存の GUI ドキュメントには接続せず、新しい解析ドキュメントを開きます。
|
||||
いいえ。REA が必要時に起動します。すでに起動している Hopper にも対応します。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA に Hopper は含まれていますか?</strong></summary>
|
||||
<details><summary><strong>REA に Hopper は含まれますか?</strong></summary>
|
||||
|
||||
含まれていません。セットアップで Hopper をインストールできますが、Hopper は独自のライセンスを持つ別製品です。REA は、エージェントが Hopper を使えるようにする CLI、MCP サーバー、ワークフローを提供します。
|
||||
含まれません。Setup で Hopper をインストールできますが、Hopper は独自のライセンスを持つ別製品です。REA は CLI、MCP サーバー、エージェント向けワークフローを提供します。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>REA は Ghidra や Java をインストールしたり同梱したりしますか?</strong></summary>
|
||||
<details><summary><strong>バイナリはアップロードされますか?</strong></summary>
|
||||
|
||||
いいえ。REA は既存の Ghidra のインストールに接続します。[要件](#要件)にある Ghidra と Java のパスを設定してから、セットアップを実行してください。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>アプリはアップロードされますか?</strong></summary>
|
||||
|
||||
REA にホスト型の解析サービスはありません。現在のプロバイダーは、成果物の解析と動作のキャプチャをローカルで行います。エージェントやモデルの提供元には独自のデータポリシーがあるため、別途確認してください。
|
||||
REA にホスト型解析サービスはありません。ローカル Unix ソケット経由で Hopper を操作します。エージェントやモデル提供者のデータポリシーは別途確認してください。
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><strong>元のソースコードを復元できますか?</strong></summary>
|
||||
|
||||
元のソースコードを確実に復元できるデコンパイラーはありません。REA はエージェントに疑似コード、アセンブリ、シンボル、文字列、メタデータ、それらの関係を提供し、観察された動作の説明や互換性のある再現に役立てられるようにします。
|
||||
保証できません。REA は疑似コード、アセンブリ、シンボル、文字列、メタデータ、関係を提供し、エージェントが観察した動作を説明または互換再現できるようにします。
|
||||
|
||||
</details>
|
||||
|
||||
@@ -366,20 +232,6 @@ REA にホスト型の解析サービスはありません。現在のプロバ
|
||||
|
||||
開発環境、アーキテクチャ、テスト、リリース手順は [CONTRIBUTING.md](CONTRIBUTING.md) を参照してください。
|
||||
|
||||
## スター履歴
|
||||
|
||||
🎉 **GitHub スターが 20,000 を突破しました。ありがとうございます!**
|
||||
|
||||
REA を使い、問題を報告し、ビルドをテストし、修正を届けてくださる皆さんに感謝します。
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA の GitHub スター履歴" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## ライセンス
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
+66
-163
@@ -4,63 +4,44 @@
|
||||
|
||||
# REA: 무엇이든 리버스 엔지니어링
|
||||
|
||||
### 에이전트로 앱의 동작부터 네이티브 바이너리까지 조사하세요.
|
||||
### 코딩 에이전트가 무엇이든 리버스 엔지니어링할 수 있게 하는 하나의 CLI 및 MCP 서버
|
||||
|
||||
**마음에 드는 기능을 찾고, 작동 방식을 이해하고, 원하는 방식으로 구현하세요.**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#조사-도구-카탈로그)
|
||||
[](https://nodejs.org/)
|
||||
[](#68개-도구로-구성된-워크벤치)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[웹사이트(영문)](https://morluto.github.io/rea/) · [가이드](https://morluto.github.io/rea/guides/) · [DX-Ball 사례](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[빠른 시작](#빠른-시작) · [현재 지원 범위](#현재-지원-범위) · [바이너리에서 동작까지](#바이너리에서-동작까지) · [조사 도구 카탈로그](#조사-도구-카탈로그) · [로드맵](#로드맵) · [작동 방식](#작동-방식)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>리버스 엔지니어링 커뮤니티에 참여하세요</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 질문과 답변 · 작업 공유</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
[빠른 시작](#빠른-시작) · [바이너리에서 동작까지](#바이너리에서-동작까지) · [68개 도구](#68개-도구로-구성된-워크벤치) · [작동 방식](#작동-방식) · [FAQ](#faq)
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="Hopper에서 분석 브리지를 시작하고 네이티브 바이너리를 조사하는 REA" width="1200" />
|
||||
<code>npx skills add morluto/rea</code>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
앱에서 마음에 드는 기능을 찾았다면 에이전트에게 REA로 조사해 달라고 요청하세요. 소스 코드가 없어도 앱을 분석하고, 기능의 동작과 근거를 설명한 뒤, 프로젝트에 맞는 기능을 구현할 수 있습니다.
|
||||
어떤 앱에서 마음에 드는 기능을 자신의 제품에도 넣고 싶나요? 소스 코드가 없어도 앱을 코딩 에이전트에 전달할 수 있습니다. REA를 사용하면 에이전트가 기능을 조사하고 작동 방식을 이해한 뒤, 사용자의 기술 스택, 디자인, 요구 사항에 맞는 버전을 구현할 수 있습니다.
|
||||
|
||||
REA는 네이티브 바이너리, JavaScript/Electron 앱, .NET 어셈블리, 웹사이트 분석 도구를 제공합니다. 에이전트와 터미널에서 같은 도구를 사용할 수 있습니다. 분석은 로컬에서 실행되며 결과에는 근거와 제한 사항이 포함됩니다.
|
||||
|
||||
Setup은 에이전트를 설정하고 기존 Hopper 또는 Ghidra 설치에 연결합니다. 승인 후 Hopper를 설치할 수도 있습니다.
|
||||
REA는 이 과정을 하나의 CLI 및 MCP 서버로 제공합니다. 에이전트는 컴파일된 앱을 조사하고 기능의 작동 방식을 추적한 뒤 알아낸 내용을 일반 코딩 작업에 활용할 수 있습니다. 복잡한 리버스 엔지니어링 도구는 REA가 하나의 인터페이스 뒤에서 처리합니다.
|
||||
|
||||
## 에이전트에게 바로 요청하기
|
||||
|
||||
[설정](#빠른-시작)을 마친 뒤 에이전트를 다시 시작하고 요청하세요.
|
||||
|
||||
```text
|
||||
메모 앱의 검색 기능이 어떻게 동작하는지 조사하고 근거를 보여 주세요.
|
||||
그다음 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
```bash
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
메모 앱 대신 조사할 앱을 지정하거나 먼저 개요를 요청할 수 있습니다.
|
||||
그런 다음 요청하세요.
|
||||
|
||||
```text
|
||||
REA를 설정하고 메모 앱을 리버스 엔지니어링해 주세요. 검색 기능의 작동 방식과
|
||||
그렇게 판단한 이유를 보여 주고, 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
```
|
||||
|
||||
메모 앱은 예시일 뿐입니다. 이해하고 싶은 앱을 지정하거나 먼저 개요부터 살펴보라고 요청할 수 있습니다.
|
||||
|
||||
## 바이너리에서 동작까지
|
||||
|
||||
@@ -72,104 +53,57 @@ REA는 분석을 바이너리 증거에 근거하게 합니다. 원본 소스
|
||||
|
||||
## REA를 사용하는 이유
|
||||
|
||||
| | |
|
||||
| ------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| **에이전트용** | 앱의 동작에 대해 질문하고 추측 대신 에이전트가 조사하게 합니다. |
|
||||
| **CLI와 MCP** | 터미널과 에이전트에서 동일한 리버스 엔지니어링 기능을 사용합니다. |
|
||||
| **안내식 설정** | 에이전트 설정, 기존 분석 도구 연결, 승인 후 Hopper 설치를 지원합니다. |
|
||||
| **통찰에서 코드로** | 기능을 이해한 뒤 같은 코딩 세션에서 자신의 제품에 맞는 버전을 구현합니다. |
|
||||
| **로컬 분석** | 분석은 지원되는 로컬 호스트에서 실행되며 REA는 바이너리를 호스팅 분석 서비스에 업로드하지 않습니다. |
|
||||
| **컨텍스트 유지** | 질문마다 분석을 처음부터 시작하지 않고 여러 앱을 연속으로 조사합니다. |
|
||||
| | |
|
||||
| ------------------ | ---------------------------------------------------------------------------------- |
|
||||
| **에이전트용** | 컴파일된 앱에 관해 질문하고 추측 대신 증거를 수집하게 합니다. |
|
||||
| **CLI와 MCP** | 터미널과 코딩 에이전트에서 동일한 리버스 엔지니어링 기능을 사용합니다. |
|
||||
| **복잡성 처리** | 도구 설정, 앱 열기, 조사 유지, 작업 후 정리를 REA가 처리합니다. |
|
||||
| **전체 조사 과정** | 첫 개요에서 의사 코드, 호출 관계, 타입, 구현 단서까지 이어서 조사합니다. |
|
||||
| **로컬 분석** | 분석은 Mac에서 실행되며 REA는 바이너리를 호스팅 분석 서비스에 업로드하지 않습니다. |
|
||||
| **컨텍스트 유지** | 질문마다 분석을 처음부터 시작하지 않고 여러 바이너리를 연속으로 조사합니다. |
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
### 설정 실행하기(권장)
|
||||
|
||||
REA를 에이전트에 연결하세요.
|
||||
### 코딩 에이전트에서 시작하기(권장)
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
Setup은 먼저 연결할 에이전트를 여러 개 선택하도록 안내합니다. 기존 REA 등록은 기본 선택되며, 감지만 된 클라이언트는 자동 선택되지 않습니다. 아직 설정되지 않은 클라이언트도 직접 선택할 수 있습니다. 정확한 경로와 변경 사항을 검토한 뒤 승인하세요. 선택한 에이전트에는 기본적으로 REA 워크플로가 설치됩니다. Hopper는 별도의 선택 사항이며 따로 승인해야 합니다. 기존 Ghidra 경로도 등록할 수 있습니다.
|
||||
에이전트에게 REA 설정을 요청하세요. Mac을 확인하고 필요한 설치 내용을 설명한 뒤 승인을 요청하며 시스템 프롬프트를 안내합니다. 전체 도구를 불러오기 위해 재시작하라고 하면 설정 후 에이전트를 재시작하세요.
|
||||
|
||||
변경 전에 계획을 표시하고 기존 설정을 백업합니다. 요구 사항과 추가 옵션은 [설치 안내](docs/installation.md)를 참고하세요.
|
||||
|
||||
### 에이전트에서 사용하기
|
||||
|
||||
설정 후 에이전트를 다시 시작하고 조사할 앱이나 기능을 설명하세요. REA는 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, Command Code, VS Code를 지원합니다. 기존 REA 등록은 기본 선택되고, 그 밖의 감지된 클라이언트는 직접 선택해야 합니다. 다른 에이전트는 아래 MCP 설정을 사용할 수 있습니다.
|
||||
|
||||
Hopper는 데모 모드로 사용할 수 있습니다. 첫 실행 안내가 나오면 데모를 선택하거나 기존 라이선스를 입력하세요.
|
||||
|
||||
### AI 코딩 어시스턴트용 스킬(선택)
|
||||
|
||||
AI 코딩 어시스턴트에 스킬을 추가하면 더 풍부한 컨텍스트를 얻을 수 있습니다.
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
이 스킬은 REA의 조사 워크플로를 제공합니다. 위 Setup을 실행해 REA를 에이전트에 연결하고 분석 도구를 설정하세요. Setup은 기본적으로 버전에 맞는 스킬을 설치합니다. 이 명령은 저장소 버전의 스킬을 설치합니다.
|
||||
|
||||
압축을 해제한 JavaScript/Electron 앱 트리나 ASAR라면 MCP 설정이나 네이티브 엔진 없이 바로 분석할 수 있습니다.
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
경로를 조사 대상의 경로로 바꾸세요(Windows에서는 `"D:/apps/example"` 같은 경로). 이 정적 워크플로에서는 이런 경로도 지원됩니다. 네이티브 분석에는 전용 엔진 설정이 필요합니다.
|
||||
|
||||
### rea 명령 설치하기
|
||||
|
||||
명령줄 도구를 설치하세요.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
Node.js와 npm이 먼저 설치되어 있어야 합니다. 터미널에서 실행하면 설치 프로그램이 `rea`를 설치하고 설정을 시작합니다.
|
||||
|
||||
npm으로 설치한 뒤 설정을 실행할 수도 있습니다.
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 요구 사항
|
||||
### 시작하기 전에
|
||||
|
||||
- macOS 12 이상
|
||||
- Ubuntu 24.04+, Fedora 41+, 64비트 Arch Linux 또는 CachyOS
|
||||
- Node.js 22.x (>=22.19), 24.x (>=24.11) 또는 26+와 npm
|
||||
- Node.js 24.18.x 및 npm 11.16.x (`nvm use`로 고정 버전 선택)
|
||||
|
||||
네이티브 바이너리 분석에는 Hopper 또는 Ghidra가 필요합니다. Hopper는 별도 소프트웨어입니다. 데모에는 공급업체의 제한이 있지만 유료 라이선스가 필수는 아닙니다.
|
||||
리버스 엔지니어링 도구를 직접 설치할 필요는 없습니다. Setup은 필요할 때 Homebrew와 [Hopper](https://www.hopperapp.com/)를 설치하고 지원되는 코딩 에이전트를 구성합니다. Hopper는 별도 소프트웨어이며 별도 라이선스가 필요합니다. Setup은 Hopper를 설치하지만 라이선스를 제공하지 않습니다.
|
||||
|
||||
Ghidra는 Linux x64와 macOS x64/arm64를 지원합니다. Ghidra 12.1.x와 그 설치본이 선언한 완전한 64비트 JDK(`application.java.min`부터 `application.java.max`까지)를 별도로 설치한 뒤 REA에서 사용하도록 설정하세요. 현재 12.1 릴리스는 JDK 21 이상을 요구하고 상한은 없습니다. 브리지는 Ghidra 12.1.4와 JDK 21에서 검증됩니다. macOS에서는 호스트 아키텍처에 맞는 네이티브 디컴파일러도 필요합니다.
|
||||
```bash
|
||||
# 1. REA 설정
|
||||
npx -y rea-agents setup --yes
|
||||
```
|
||||
|
||||
Setup은 설치를 확인하고 경로를 저장할 수 있습니다. Ghidra, Java, Node.js, npm, Homebrew를 설치하거나 업데이트하지 않습니다.
|
||||
macOS나 설치 프로그램이 확인을 요청하면 해당 절차를 완료하고 같은 명령을 다시 실행하세요.
|
||||
|
||||
저장소 main과 npm 4.1.0에는 로컬 NTFS상의 네이티브 x86-64 PE 애플리케이션(비관리, 비 DLL 대상)을 위한 실험적인 Windows x64 Ghidra P0 지원이 포함되어 있습니다. Job Object, 비공개 DACL, 경로 승인 제어가 함께 제공됩니다. 이전 npm 패키지에 이 기능이 있을 것으로 기대하기 전에 [릴리스 경계](docs/installation.md#released-package-and-main)를 확인하세요. 전제 조건과 검증 범위는 [Windows Ghidra P0](docs/windows-ghidra-p0.md)를 참고하세요.
|
||||
### 2. 코딩 에이전트 재시작
|
||||
|
||||
### 문제 해결
|
||||
Setup은 감지된 Claude Desktop과 Cursor를 자동으로 구성합니다. REA를 불러오도록 앱을 재시작하세요.
|
||||
|
||||
`npx -y rea-agents@latest doctor`는 호스트, 의존성, 분석 도구, 에이전트 설정을 변경 없이 확인합니다. 구조화된 진단에는 `--json`을 추가하세요.
|
||||
### 3. 에이전트에게 요청
|
||||
|
||||
Linux에서는 실행 가능한 `/opt/hopper/bin/Hopper`를 우선 사용하고, 사용할 수 없으면 `~/.local/share/rea/hopper/bin/Hopper`를 자동으로 확인합니다. 다른 위치에는 `HOPPER_LAUNCHER_PATH`를 설정하세요. 파일이 있는데도 분석 엔진이 없다고 하면 실제 Hopper 경로에 대해 `ldd /absolute/path/to/Hopper | grep 'not found'`를 실행해 누락된 라이브러리를 확인하세요. 자세한 내용은 [Hopper 안내](docs/installation.md#hopper)를 참고하세요.
|
||||
앱 이름만 말해도 됩니다. 코딩 에이전트가 앱을 찾아 REA에 필요한 프로그램 파일을 전달합니다.
|
||||
|
||||
### 업데이트와 제거
|
||||
```text
|
||||
REA로 메모 앱을 리버스 엔지니어링해 주세요. 검색 기능의 작동 방식과 증거를 보여 주고,
|
||||
SQLite를 사용해 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
```
|
||||
|
||||
- `rea update`는 현재 REA 설치를 업데이트합니다.
|
||||
- `rea uninstall`은 REA가 관리하는 에이전트 등록과 워크플로 파일을 제거합니다. Hopper는 유지합니다.
|
||||
- `rea uninstall --purge-data`는 REA 캐시와 상태도 삭제합니다. 해당 데이터를 지우려는 경우에만 사용하세요.
|
||||
문제가 있으면 다음을 실행하세요.
|
||||
|
||||
## 현재 지원 범위
|
||||
|
||||
저장소의 현재 기능과 플랫폼 요구 사항은 [영문 지원 가이드](README.md#current-status)에 설명되어 있습니다. main은 [npm 릴리스](docs/installation.md#released-package-and-main)보다 앞선 내용이 포함될 수 있습니다.
|
||||
|
||||
- Ghidra는 Linux x64, macOS x64/arm64, 실험적인 Windows x64 P0 경계에서 25개의 읽기 전용 작업을 제공합니다. Linux/macOS에서는 원자적 세션 함수 주석도 추가로 지원합니다. Windows P0는 읽기 전용이며 Ghidra에는 GUI 제어가 없습니다.
|
||||
- 정적 Android 검사는 별도로 준비한 JADX/Java가 필요합니다. 실제 제공자 검증은 Linux를 대상으로 합니다. [Android 분석](docs/android-analysis.md)을 참고하세요.
|
||||
- 브라우저, Electron, 프로세스 요청은 대상·동작·수명 주기를 직접 지정하며 호스트의 실제 접근에 의존합니다. 별도의 REA 권한 승인은 필요 없습니다. Setup의 설정 쓰기와 Hopper 설치는 여전히 정확한 계획에 대한 승인이 필요합니다.
|
||||
- `rea capabilities`는 모든 앱/런타임 도구의 목록이 아니라 바이너리 세션 작업을 설명합니다. 연결된 MCP 도구 목록과 관련 가이드를 참고하세요.
|
||||
```bash
|
||||
npx -y rea-agents doctor
|
||||
```
|
||||
|
||||
## 하나의 프롬프트로 전체 조사
|
||||
|
||||
@@ -187,7 +121,7 @@ TypeScript와 SQLite를 사용해 제 프로젝트에 맞는 버전을 구현해
|
||||
| 5 | 관련 루틴 디컴파일 | `procedure_pseudo_code`, `procedure_assembly`, `batch_decompile` |
|
||||
| 6 | 프로젝트에 기능 구현 | 기술 스택, 제품, 요구 사항에 맞는 코드 |
|
||||
|
||||
REA는 1~5단계의 바이너리 분석을 처리합니다. 6단계는 에이전트가 일반 파일 편집 및 테스트 도구로 수행합니다.
|
||||
REA는 1–5단계의 바이너리 분석을 처리합니다. 6단계는 에이전트가 일반 파일 편집 및 테스트 도구로 수행합니다.
|
||||
|
||||
## 에이전트가 할 수 있는 일
|
||||
|
||||
@@ -201,69 +135,52 @@ REA는 1~5단계의 바이너리 분석을 처리합니다. 6단계는 에이전
|
||||
- Swift 및 Objective-C 메타데이터를 분석합니다.
|
||||
- Hopper에 이름, 주석, 북마크를 남겨 사람과 에이전트의 분석을 연결합니다.
|
||||
|
||||
## 조사 도구 카탈로그
|
||||
## 68개 도구로 구성된 워크벤치
|
||||
|
||||
| 도구 분류 | 수 | 용도 |
|
||||
| ----------------- | --: | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 네이티브 검사 | 41 | 함수, 의사 코드, 어셈블리, 문자열, 심볼, 호출, 참조, 주석, 바이트 읽기, 파일 오프셋 |
|
||||
| 조사 워크플로 | 14 | 앱 개요, 함수 기록서, 네이티브 API와 디스패치, 일괄 디컴파일, 기능 추적, 호출 경로, 호출 그래프, Swift와 Objective-C 탐색 |
|
||||
| 네이티브 바이너리 | 10 | macOS의 Mach-O 메타데이터, 서명, plist, 아키텍처, Swift 심볼과 LLDB 호출 관찰; Linux의 ELF 레이아웃, 정적 보호 증거와 기록된 크래시 |
|
||||
| 아티팩트 그래프 | 7 | 디렉터리와 패키지 목록, 컴파일된 Interface Builder 파일, Apple 애셋 카탈로그, Mach-O dylib 로드 해석, 추출, 오프라인 EVM 인터페이스 추론 |
|
||||
| 관리 PE/CLI | 7 | .NET 식별, 메타데이터, CIL 명령어, 네이티브 종속성, 재구성 가져오기, 빌드 비교 |
|
||||
| 펌웨어 | 2 | Linux 펌웨어 영역 검사 및 명시적 추출 |
|
||||
| Android APK | 5 | 패키지와 manifest 선언, 클래스 검색, 멤버 목록, 메서드 디컴파일, 정적 참조 |
|
||||
| 브라우저 관찰 | 12 | 페이지 구조, 네트워크 메타데이터, 스크립트, 소스 맵, WebMCP 탐색, 스크린샷, 캡처 비교 |
|
||||
| Electron 분석 | 5 | 렌더러 관찰, 정적 앱 매핑, 정적·런타임 결과 연결 |
|
||||
| JavaScript 런타임 | 2 | Node/Electron Inspector 대상 탐색, 스크립트 위치, 실행 컨텍스트 이벤트 |
|
||||
| 앱 워크플로 | 13 | 캡처한 웹 스크립트 내보내기, 계층 간 기능 추적, 빌드 비교, 히스토리 소스 매핑, 정적 반환 구조 비교, 재구현 검증 |
|
||||
| 작업 공간과 관찰 | 21 | 세션, 근거 번들, 탐색 컨텍스트, 프로세스·아티팩트·함수 비교, 미해결 항목 기록 |
|
||||
|
||||
## 로드맵
|
||||
|
||||
다음 작업은 네이티브 대상 검증 확대, JavaScript와 .NET의 버전 비교, 런타임 관찰과 재구현 검증입니다. Setup은 에이전트 연결과 Hopper 설치 선택을 이미 지원합니다. 다른 분석 도구 설치는 향후 작업입니다. [설치 로드맵](docs/roadmap.md)과 [분석 도구 평가](docs/provider-evaluation.md)를 참고하세요.
|
||||
| 도구 그룹 | 수 | 예시 |
|
||||
| ------------------- | --: | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| 바이너리 검사 | 33 | 프로시저, 의사 코드, 어셈블리, 문자열, 이름, 세그먼트, callers, callees, xrefs, 주석 |
|
||||
| 합성 분석 | 10 | `binary_overview`, `analyze_function`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift 및 ObjC 탐색 |
|
||||
| macOS 네이티브 도구 | 5 | Mach-O 메타데이터, 코드 서명, plist, 아키텍처, Swift 디맹글링. Hopper 실행 불필요 |
|
||||
| 아티팩트 그래프 | 2 | 디렉터리, ZIP/APK/IPA, ASAR 결정적 인벤토리와 명시적으로 선택한 트랜잭션 추출 |
|
||||
| 바이너리 세션 | 18 | `open_binary`, `binary_session`, 증거 번들, 프로세스·아티팩트·함수 비교, 잔여 미지수 레지스트리 |
|
||||
|
||||
## 다른 코딩 에이전트에서 사용하기
|
||||
|
||||
Setup은 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, Command Code, VS Code를 지원합니다. 기존 REA 등록은 기본 선택되고, 그 밖의 감지된 클라이언트는 직접 선택해야 합니다. 로컬 MCP 서버를 지원하는 에이전트는 다음 설정으로 연결할 수 있습니다.
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
Setup은 현재 Claude Desktop과 Cursor를 자동으로 구성합니다. 로컬 MCP 서버를 지원하는 코딩 에이전트라면 다음 설정으로 REA를 사용할 수 있습니다.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@6.0.0", "mcp"]
|
||||
"args": ["-y", "rea-agents", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## 작동 방식
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["코딩 에이전트"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal["터미널"] --> REA
|
||||
REA --> Hopper["Hopper 분석 작업"]
|
||||
REA --> Hopper["분석 엔진"]
|
||||
Hopper --> App["사용자의 앱"]
|
||||
REA --> Ghidra["Ghidra 읽기 전용 분석<br/>인벤토리 + 함수 분석"]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI와 MCP 서버는 같은 분석 워크플로를 사용합니다. 터미널 명령은 완료 후 자신의 브리지 세션을 해제하고, 에이전트 세션은 조사 중 연결을 유지할 수 있습니다. REA 세션을 닫아도 사용자가 이용 중인 Hopper 앱은 종료하지 않습니다.
|
||||
CLI와 MCP 서버는 같은 분석 엔진을 사용합니다. 터미널 명령은 끝날 때 앱을 닫고, 에이전트 세션은 조사하는 동안 앱을 열어 둡니다.
|
||||
|
||||
## CLI
|
||||
|
||||
위의 에이전트 워크플로가 REA를 사용하는 가장 쉬운 방법입니다. 터미널에서 앱을 한 번 빠르게 살펴보려면 다음을 실행하세요.
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
직접 디컴파일하는 방법과 기타 옵션은 `npx -y rea-agents@latest --help`에서 확인할 수 있습니다.
|
||||
직접 디컴파일하는 방법과 기타 옵션은 `npx -y rea-agents --help`에서 확인할 수 있습니다.
|
||||
|
||||
전역 `rea` 명령으로 설치할 수도 있습니다.
|
||||
|
||||
@@ -273,7 +190,7 @@ rea --help
|
||||
rea mcp
|
||||
```
|
||||
|
||||
REA는 macOS의 `.app` 폴더를 직접 열 수 있습니다. 에이전트가 앱을 찾지 못하면 설치 위치를 알려 주세요.
|
||||
REA는 Mac의 `.app` 폴더를 직접 열 수 있습니다. 에이전트가 앱을 찾지 못하면 설치 위치를 알려 주세요.
|
||||
|
||||
## Hopper 앱 동작
|
||||
|
||||
@@ -283,13 +200,13 @@ REA는 필요할 때 Hopper를 시작합니다. Hopper 런처는 내부적으로
|
||||
|
||||
## 보안 모델
|
||||
|
||||
각 세션은 무작위 capability token과 현재 사용자 전용 Unix 소켓을 사용합니다. Ghidra 세션은 격리된 임시 프로젝트도 사용하며 사용자 소유 Ghidra 프로젝트를 열거나 수정하지 않습니다. 이는 샌드박스가 아니며 같은 운영 체제 사용자로 실행되는 악성 프로세스를 방어하지 않습니다. 신뢰할 수 없는 바이너리를 열면 선택한 로컬 공급자가 현재 사용자 권한으로 분석합니다. 취약점은 [SECURITY.md](SECURITY.md)의 비공개 절차로 신고하세요.
|
||||
각 세션은 무작위 capability token과 현재 사용자 전용 Unix 소켓을 사용합니다. 이는 샌드박스가 아니며 같은 사용자로 실행되는 악성 프로세스를 방어하지 않습니다. 신뢰할 수 없는 바이너리를 열면 현재 macOS 사용자 권한으로 Hopper가 분석합니다. 취약점은 [SECURITY.md](SECURITY.md)의 비공개 절차로 신고하세요.
|
||||
|
||||
## FAQ
|
||||
|
||||
<details><summary><strong>Hopper를 미리 실행해야 하나요?</strong></summary>
|
||||
|
||||
아니요. REA가 필요할 때 시작합니다. Hopper가 실행 중이어도 사용할 수 있지만 기존 GUI 문서에 연결하는 대신 새 분석 문서를 엽니다.
|
||||
아니요. REA가 필요할 때 시작하며 이미 실행 중인 Hopper도 지원합니다.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -315,20 +232,6 @@ REA에는 호스팅 분석 서비스가 없습니다. 로컬 Unix 소켓을 통
|
||||
|
||||
개발 환경, 아키텍처, 테스트, 릴리스 지침은 [CONTRIBUTING.md](CONTRIBUTING.md)를 참고하세요.
|
||||
|
||||
## 스타 기록
|
||||
|
||||
🎉 **GitHub 스타 20,000개, 감사합니다!**
|
||||
|
||||
REA를 사용하고, 버그를 제보하고, 빌드를 테스트하고, 수정에 기여해 주신 모든 분께 감사드립니다.
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA GitHub 스타 기록" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 라이선스
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
+65
-161
@@ -4,62 +4,44 @@
|
||||
|
||||
# REA:逆向工程一切
|
||||
|
||||
### 使用智能体逆向工程任何程序,从应用行为到原生二进制文件。
|
||||
### 一个 CLI 与 MCP 服务器,让编程智能体逆向工程任何程序
|
||||
|
||||
**看到喜欢的功能。理解它的原理。按照你的方式实现。**
|
||||
|
||||
[](https://www.npmjs.com/package/rea-agents)
|
||||
[](https://github.com/morluto/rea/actions/workflows/ci.yml)
|
||||
[](#调查工具目录)
|
||||
[](https://nodejs.org/)
|
||||
[](#68-个工具组成的工作台)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[网站(英文)](https://morluto.github.io/rea/) · [使用指南](https://morluto.github.io/rea/guides/) · [DX-Ball 案例](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[快速开始](#快速开始) · [当前状态](#当前状态) · [从二进制到行为](#从二进制到行为) · [调查工具目录](#调查工具目录) · [路线图](#路线图) · [工作原理](#工作原理)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>加入逆向工程社区</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 问答 · 成果分享</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
[快速开始](#快速开始) · [从二进制到行为](#从二进制到行为) · [68 个工具](#68-个工具组成的工作台) · [工作原理](#工作原理) · [常见问题](#常见问题)
|
||||
|
||||
<br />
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA 在 Hopper 中启动分析桥并检查原生二进制文件" width="1200" />
|
||||
<code>npx skills add morluto/rea</code>
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
看到某个应用中想加入自己产品的功能?让智能体用 REA 调查它。即使没有源代码,智能体也可以检查应用、解释功能的工作方式、展示证据,并为你的项目实现类似功能。
|
||||
看到某个应用中想加入自己产品的功能?即使没有源代码,也可以把应用交给编程智能体。借助 REA,智能体能够调查该功能、理解其工作原理,并按照你的技术栈、设计和需求构建适合你产品的版本。
|
||||
|
||||
REA 提供原生二进制、JavaScript 与 Electron 应用、.NET 程序集和网站的分析工具。你可以从智能体或终端使用它们。分析在本机运行,结果会说明证据和限制。
|
||||
|
||||
Setup 可以配置智能体、连接已安装的 Hopper 或 Ghidra,也可以在你批准后安装 Hopper。
|
||||
REA 通过一个 CLI 与 MCP 服务器实现这套流程。智能体可以检查编译后的应用、追踪功能的工作方式,并把了解到的内容用于日常编码。REA 在一个接口背后处理复杂的逆向工程工具。
|
||||
|
||||
## 直接询问智能体
|
||||
|
||||
完成[设置](#快速开始)后,重启智能体并提出问题:
|
||||
|
||||
```text
|
||||
解释“备忘录”应用的搜索功能,展示证据,然后为我的项目实现类似功能。
|
||||
```bash
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
把“备忘录”替换成你想了解的应用,也可以先请求概览。
|
||||
然后说:
|
||||
|
||||
```text
|
||||
设置 REA 并逆向工程“备忘录”应用。解释搜索功能的工作方式,
|
||||
说明你是如何得出结论的,然后为我的项目构建类似功能。
|
||||
```
|
||||
|
||||
“备忘录”只是示例。你可以说出任何想了解的应用,也可以让智能体先从概览开始。
|
||||
|
||||
## 从二进制到行为
|
||||
|
||||
@@ -71,104 +53,57 @@ REA 让调查始终以二进制证据为依据。它不会声称能恢复原始
|
||||
|
||||
## 为什么选择 REA
|
||||
|
||||
| | |
|
||||
| ---------------- | -------------------------------------------------------------------- |
|
||||
| **为智能体设计** | 直接询问应用的行为,让智能体去检查,而不是猜测。 |
|
||||
| **CLI 与 MCP** | 在终端或智能体中使用同一套逆向工程能力。 |
|
||||
| **引导式设置** | 配置智能体、连接已有分析工具,或在你批准后安装 Hopper。 |
|
||||
| **从洞察到代码** | 理解功能后,在同一次编程会话中构建属于你自己的版本。 |
|
||||
| **本地运行** | 分析在受支持的本机系统上运行;REA 不会把二进制上传到托管式分析服务。 |
|
||||
| **保留上下文** | 连续调查多个应用,无需为每个问题重新开始整个分析过程。 |
|
||||
| | |
|
||||
| ---------------- | -------------------------------------------------------------- |
|
||||
| **为智能体设计** | 直接询问编译后应用的行为,让智能体搜集证据,而不是猜测。 |
|
||||
| **CLI 与 MCP** | 在终端或编程智能体中使用同一套逆向工程能力。 |
|
||||
| **处理复杂流程** | REA 负责工具设置、打开应用、维持调查过程,并在完成后清理资源。 |
|
||||
| **完整工作流** | 从初步概览推进到伪代码、调用关系、类型和实现线索。 |
|
||||
| **本地运行** | 分析在你的 Mac 上运行;REA 不会把二进制上传到托管式分析服务。 |
|
||||
| **保留上下文** | 连续调查多个二进制文件,无需为每个问题重新开始整个分析过程。 |
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 运行设置(推荐)
|
||||
|
||||
配置 REA 与智能体的连接:
|
||||
### 使用编程智能体(推荐)
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
npx skills add morluto/rea
|
||||
```
|
||||
|
||||
Setup 首先让你多选要连接的智能体。已有 REA 注册默认选中;仅被检测到的客户端不会自动选中,也可以手动选择尚未配置的客户端。查看具体路径和变更后再批准。所选智能体默认安装 REA 工作流;Hopper 是单独的可选操作,需要单独批准。Setup 也可以记录现有 Ghidra 的路径。
|
||||
让智能体设置 REA。它会检查你的 Mac,说明需要安装的内容,先征得同意,并引导你处理系统提示。如果智能体要求重启以加载完整工具,请在设置后重启。
|
||||
|
||||
Setup 会先展示变更并备份已有配置。要求与更多选项见[安装与设置](docs/installation.md)。
|
||||
|
||||
### 使用智能体
|
||||
|
||||
设置完成后,重启智能体并描述你想了解的应用或功能。REA 支持 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code 和 VS Code。已有 REA 注册默认选中;其他检测到的客户端需手动选择。其他智能体可以使用下方的 MCP 配置。
|
||||
|
||||
Hopper 支持演示模式。如果首次启动时出现提示,选择演示模式或输入已有许可证。
|
||||
|
||||
### AI 编程助手技能(可选)
|
||||
|
||||
为你的 AI 编程助手添加 skill,以获得更丰富的上下文:
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
该 skill 提供 REA 的调查工作流。先运行上文的 setup,把 REA 连接到你的智能体并配置分析工具。Setup 默认会安装版本匹配的 skill;这条命令安装的是仓库版本。
|
||||
|
||||
对于已解包的 JavaScript/Electron 应用目录或 ASAR,无需 MCP 配置,也无需原生引擎:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
把路径换成你的目标(例如 Windows 上的 `"D:/apps/example"`)。这条静态流程支持此类路径。原生分析仍需要先配置对应的分析引擎。
|
||||
|
||||
### 安装 rea 命令
|
||||
|
||||
安装命令行工具:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
需要预先安装 Node.js 和 npm。在终端中运行时,安装程序会安装 `rea` 并启动设置。
|
||||
|
||||
也可以用 npm 安装,再运行设置:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 系统要求
|
||||
### 开始之前
|
||||
|
||||
- macOS 12 或更高版本
|
||||
- Ubuntu 24.04+、Fedora 41+、64 位 Arch Linux 或 CachyOS
|
||||
- Node.js 22.x (>=22.19)、24.x (>=24.11) 或 26+,以及 npm
|
||||
- Node.js 24.18.x 和 npm 11.16.x(运行 `nvm use` 选择固定版本)
|
||||
|
||||
原生二进制分析需要 Hopper 或 Ghidra。Hopper 是独立软件;演示模式有厂商规定的限制,不要求购买许可证。
|
||||
你不需要手动安装逆向工程工具。Setup 会在需要时安装 Homebrew 和 [Hopper](https://www.hopperapp.com/),然后配置支持的编程智能体。Hopper 是独立软件,需要单独授权;Setup 可以安装它,但不提供许可证。
|
||||
|
||||
Ghidra 支持 Linux x64 和 macOS x64/arm64。单独安装 Ghidra 12.1.x,以及该安装在 `application.java.min` 到 `application.java.max` 中声明的完整 64 位 JDK,然后配置 REA 使用它们。当前 12.1 发行版要求 JDK 21 或更新版本,并且不设置上限。桥接在 Ghidra 12.1.4 和 JDK 21 上完成验证。macOS 还需要对应架构的原生反编译器。
|
||||
```bash
|
||||
# 1. 安装和配置 REA
|
||||
npx -y rea-agents setup --yes
|
||||
```
|
||||
|
||||
Setup 可以验证安装并保存路径,不会安装或升级 Ghidra、Java、Node.js、npm 或 Homebrew。
|
||||
如果 macOS 或安装程序要求确认,请完成提示,然后再次运行同一命令。
|
||||
|
||||
仓库 main 分支与 npm 4.1.0 已包含实验性的 Windows x64 Ghidra P0 支持,适用于本地 NTFS 上的原生 x86-64 PE 应用(非托管、非 DLL),并附带 Job Object、私有 DACL 与路径准入控制。请先确认[发布边界](docs/installation.md#released-package-and-main),再假定旧版 npm 包具备该能力。详见 [Windows Ghidra P0](docs/windows-ghidra-p0.md) 的前置条件与已验证范围。
|
||||
### 2. 重启编程智能体
|
||||
|
||||
### 故障排除
|
||||
Setup 会自动配置检测到的 Claude Desktop 和 Cursor。重启应用,让它加载 REA。
|
||||
|
||||
运行 `npx -y rea-agents@latest doctor`,检查主机、依赖、分析工具和智能体配置。该命令不会修改文件。添加 `--json` 可获取结构化诊断。
|
||||
### 3. 直接询问智能体
|
||||
|
||||
Linux 上 REA 会优先使用可执行的 `/opt/hopper/bin/Hopper`;如果不可用,则自动检查 `~/.local/share/rea/hopper/bin/Hopper`。其他路径可通过 `HOPPER_LAUNCHER_PATH` 指定。如果文件存在但仍报告缺少分析引擎,请对实际 Hopper 路径运行 `ldd /absolute/path/to/Hopper | grep 'not found'` 检查缺少的系统库。安装详情见 [Hopper 指南](docs/installation.md#hopper)。
|
||||
你可以直接说出应用名称。编程智能体会找到应用,并把 REA 所需的程序文件交给它。
|
||||
|
||||
### 更新与卸载
|
||||
```text
|
||||
使用 REA 逆向工程“备忘录”应用。解释搜索功能的工作方式,展示证据,
|
||||
然后使用 SQLite 为我的项目构建一个类似功能。
|
||||
```
|
||||
|
||||
- `rea update` 更新当前 REA 安装。
|
||||
- `rea uninstall` 移除 REA 管理的智能体配置项和工作流文件,保留 Hopper。
|
||||
- `rea uninstall --purge-data` 还会删除 REA 的缓存和状态。仅在需要移除这些数据时使用。
|
||||
如果遇到问题,请运行:
|
||||
|
||||
## 当前状态
|
||||
|
||||
仓库当前能力与平台要求见[英文支持指南](README.md#current-status)。main 分支可能领先于 [npm 发布版](docs/installation.md#released-package-and-main)。
|
||||
|
||||
- Ghidra 在 Linux x64、macOS x64/arm64 以及实验性的 Windows x64 P0 边界上提供 25 个只读操作。Linux/macOS 额外支持原子化的会话级函数注释。Windows P0 为只读;Ghidra 不提供 GUI 控制。
|
||||
- 静态 Android 检查需要自备 JADX/Java,前置条件独立;真机验证覆盖 Linux。详见 [Android 分析](docs/android-analysis.md)。
|
||||
- 浏览器、Electron 与进程请求需直接声明目标、动作与生命周期,并依赖宿主的实际访问权限,无需 REA 另行授权。Setup 的配置写入与 Hopper 安装仍需逐项批准确切计划。
|
||||
- `rea capabilities` 描述的是二进制会话操作,而非全部应用/运行时工具。请以已连接 MCP 工具列表与相关指南为准。
|
||||
```bash
|
||||
npx -y rea-agents doctor
|
||||
```
|
||||
|
||||
## 一个提示词,完成一次完整调查
|
||||
|
||||
@@ -200,69 +135,52 @@ REA 负责第 1–5 步中的二进制分析。第 6 步由智能体使用其常
|
||||
- 分析 Swift 和 Objective-C 元数据。
|
||||
- 在 Hopper 中留下名称、注释与书签,使人与智能体的分析互相增强。
|
||||
|
||||
## 调查工具目录
|
||||
## 68 个工具组成的工作台
|
||||
|
||||
| 工具类别 | 数量 | 用途 |
|
||||
| ----------------- | ---: | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| 原生检查 | 41 | 函数、伪代码、汇编、字符串、符号、调用、引用、注释、字节读取与文件偏移 |
|
||||
| 调查工作流 | 14 | 应用概览、函数档案、原生 API 与分发、批量反编译、功能追踪、调用路径与调用图、Swift 与 Objective-C 发现 |
|
||||
| 原生二进制工具 | 10 | macOS 的 Mach-O 元数据、签名、plist、架构、Swift 符号与 LLDB 调用观察;Linux 的 ELF 布局、静态保护证据与历史崩溃记录 |
|
||||
| 产物图 | 7 | 目录与软件包清单、编译后的 Interface Builder 文件、Apple 资源目录、Mach-O dylib 加载解析、提取与离线 EVM 接口推断 |
|
||||
| 托管 PE/CLI | 7 | .NET 程序集身份、元数据、CIL 指令、原生依赖声明、重建导入与构建比较 |
|
||||
| 固件 | 2 | Linux 固件区域检查与显式提取 |
|
||||
| Android APK | 5 | 包与 manifest 声明、类搜索、成员清单、方法反编译与静态引用 |
|
||||
| 浏览器观察 | 12 | 页面结构、网络元数据、脚本、来源映射、WebMCP 发现、截图与捕获比较 |
|
||||
| Electron 分析 | 5 | 渲染进程观察、静态应用映射、静态/运行时结果关联 |
|
||||
| JavaScript 运行时 | 2 | Node/Electron Inspector 目标发现、脚本位置与执行上下文事件 |
|
||||
| 应用工作流 | 13 | 捕获的网站脚本导出、跨层功能追踪、构建比较、历史源码映射、静态返回结构比较与重建验证 |
|
||||
| 工作区与观察 | 21 | 会话、证据包、导航上下文、进程/产物/函数比较与待解决问题跟踪 |
|
||||
|
||||
## 路线图
|
||||
|
||||
接下来的工作包括更多原生目标验证、JavaScript 与 .NET 跨版本比较,以及运行时观察和重建验证。当前设置已支持选择智能体集成和 Hopper 安装;安装其他分析工具仍是后续工作。详见[安装路线图](docs/roadmap.md)和[分析工具评估](docs/provider-evaluation.md)。
|
||||
| 工具类别 | 数量 | 示例 |
|
||||
| -------------- | ---: | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| 二进制检查 | 33 | 过程、伪代码、汇编、字符串、名称、段、调用者、被调用者、交叉引用、注释 |
|
||||
| 组合分析 | 10 | `binary_overview`, `analyze_function`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`、Swift 与 ObjC 发现 |
|
||||
| macOS 原生工具 | 5 | Mach-O 元数据、代码签名、plist、架构与 Swift 符号还原,无需启动 Hopper |
|
||||
| 制品图谱 | 2 | 确定性目录、ZIP/APK/IPA 与 ASAR 清单,以及显式选择的事务式提取 |
|
||||
| 二进制会话 | 18 | `open_binary`、`binary_session`、证据包、进程、制品与函数比较、残余未知项注册表 |
|
||||
|
||||
## 与其他编程智能体一起使用
|
||||
|
||||
Setup 支持 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code 和 VS Code。已有 REA 注册默认选中;其他检测到的客户端需手动选择。任何支持本地 MCP 服务器的智能体都可以使用以下配置连接 REA。
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
Setup 目前会自动配置 Claude Desktop 和 Cursor。任何支持本地 MCP 服务器的编程智能体都可以使用以下配置连接 REA。
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@6.0.0", "mcp"]
|
||||
"args": ["-y", "rea-agents", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
## 工作原理
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Agent["编程智能体"] --> REA["REA<br/>CLI + MCP"]
|
||||
Terminal["终端"] --> REA
|
||||
REA --> Hopper["Hopper 分析操作"]
|
||||
REA --> Hopper["分析引擎"]
|
||||
Hopper --> App["你的应用"]
|
||||
REA --> Ghidra["Ghidra 只读分析<br/>清单 + 函数分析"]
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI 与 MCP 服务器使用相同的分析流程。终端命令完成后释放自己创建的桥接会话;智能体会话可以在调查期间保持连接。关闭 REA 会话不会退出用户正在使用的 Hopper 应用。
|
||||
CLI 与 MCP 服务器使用相同的分析引擎。终端命令完成后会关闭应用;智能体会话则会在调查期间保持应用打开。
|
||||
|
||||
## CLI
|
||||
|
||||
上面的智能体工作流是使用 REA 最简单的方式。如果只想在终端中快速了解一个应用:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze /Applications/Notes.app
|
||||
npx -y rea-agents analyze /Applications/Notes.app
|
||||
```
|
||||
|
||||
运行 `npx -y rea-agents@latest --help` 查看直接反编译和其他选项。
|
||||
运行 `npx -y rea-agents --help` 查看直接反编译和其他选项。
|
||||
|
||||
也可以全局安装 `rea` 命令:
|
||||
|
||||
@@ -282,15 +200,15 @@ REA 会推导明确的格式和架构参数,以避免常见的 FAT 与 ARM 选
|
||||
|
||||
## 安全模型
|
||||
|
||||
每个桥会话都使用随机能力令牌和仅限当前用户的 Unix 套接字。诊断保留本机路径、摘要和错误位置等排查信息,同时移除凭据和认证令牌。Ghidra 会话还使用隔离的临时项目,不会打开或修改用户自己的 Ghidra 项目。
|
||||
每个桥会话都使用随机能力令牌和仅限当前用户的 Unix 套接字。协议消息有大小限制,面向调用者的错误不会暴露启动器 stderr 或内部异常原因。
|
||||
|
||||
这不是沙箱,也无法防御以同一操作系统用户身份运行的恶意进程。打开不可信二进制文件会让所选本地提供商以当前用户权限进行解析和分析。请按照 [SECURITY.md](SECURITY.md) 中的私密流程报告漏洞。
|
||||
这不是沙箱,也无法防御以同一 macOS 用户身份运行的恶意进程。打开不可信二进制文件会让 Hopper 以当前用户权限进行解析和分析。请按照 [SECURITY.md](SECURITY.md) 中的私密流程报告漏洞。
|
||||
|
||||
## 常见问题
|
||||
|
||||
<details><summary><strong>Hopper 是否需要提前运行?</strong></summary>
|
||||
|
||||
不需要。REA 会在操作需要时启动 Hopper。Hopper 已经运行时也可以使用,但 REA 会打开新的分析文档,不会连接并复用现有 GUI 文档。
|
||||
不需要。REA 会在操作需要时启动 Hopper,也支持已经运行的 Hopper。
|
||||
|
||||
</details>
|
||||
|
||||
@@ -316,20 +234,6 @@ REA 不提供托管分析服务,而是通过本地 Unix 套接字把操作交
|
||||
|
||||
开发环境、架构、测试和发布说明请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
|
||||
## Star 历史
|
||||
|
||||
🎉 **GitHub Star 突破 20,000,感谢大家!**
|
||||
|
||||
感谢每一位使用 REA、反馈问题、测试构建和贡献修复的朋友。
|
||||
|
||||
<a href="https://www.star-history.com/?repos=morluto%2Frea&type=date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
<img alt="REA GitHub Star 历史" src="https://api.star-history.com/chart?repos=morluto/rea&type=date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 许可证
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
+4
-32
@@ -6,42 +6,14 @@ Security fixes are provided for the latest published version of REA. Upgrade to
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Do not disclose vulnerabilities, proof-of-concept exploits, credentials,
|
||||
private binaries, Hopper documents, or private Ghidra projects in a public
|
||||
issue.
|
||||
Do not disclose vulnerabilities, proof-of-concept exploits, credentials, private binaries, or Hopper documents in a public issue.
|
||||
|
||||
Use **Report a vulnerability** in the repository's **Security** tab to submit the report privately. Do not open a public issue to request a private contact channel.
|
||||
|
||||
Include the affected REA version, operating system/distribution, selected
|
||||
provider and version, impact, reproduction conditions, and any suggested
|
||||
mitigation in the private report. Remove credentials, proprietary binaries,
|
||||
Hopper documents, Ghidra project contents, socket capability tokens, and
|
||||
unrelated local paths from logs or examples.
|
||||
Include the affected REA version, macOS and Hopper versions, impact, reproduction conditions, and any suggested mitigation in the private report. Remove credentials, proprietary binaries, Hopper documents, socket capability tokens, and unrelated local paths from logs or examples.
|
||||
|
||||
The maintainer will acknowledge the report, assess its scope, and coordinate
|
||||
remediation and disclosure timing with the reporter. Please allow time for
|
||||
provider-dependent behavior to be reproduced safely.
|
||||
The maintainer will acknowledge the report, assess its scope, and coordinate remediation and disclosure timing with the reporter. Please allow time for Hopper-dependent behavior to be reproduced safely.
|
||||
|
||||
## Security boundary
|
||||
|
||||
REA authenticates each local provider bridge session with a random capability
|
||||
token and a current-user Unix socket. Tokens are transferred through private
|
||||
session descriptors rather than process arguments or environment variables.
|
||||
This is not a sandbox and does not protect against malicious processes already
|
||||
running as the same operating-system user. Opening an untrusted binary
|
||||
delegates parsing and analysis to the selected local provider with that user's
|
||||
permissions.
|
||||
|
||||
Ghidra sessions use a packaged Java `HeadlessScript`, an isolated temporary
|
||||
project, and private home, cache, and runtime directories. REA imports only the
|
||||
requested target, bounds startup and protocol messages, requests bounded CPU
|
||||
and heap settings, and deletes the temporary project when the session closes.
|
||||
These controls limit accidental persistence and resource use; they do not make
|
||||
Ghidra a sandbox. REA does not open or modify user-owned Ghidra projects.
|
||||
|
||||
`GHIDRA_INSTALL_DIR` and optional `JAVA_HOME` identify user-managed
|
||||
installations. Setup can add those values to detected client configuration only
|
||||
after showing its plan and receiving approval. REA does not download, install,
|
||||
upgrade, or modify Ghidra or Java.
|
||||
|
||||
On Linux, REA accepts release metadata only from Hopper's HTTPS endpoint, restricts package URLs to Hopper's public download origin, bounds the package size, and compares the downloaded bytes with Hopper's published checksum before requesting installation. The published SHA-1 value is a corruption check, not a modern package signature; HTTPS origin validation remains part of the trust boundary. Dependency installation is delegated without shell evaluation to `apt-get`, `dnf`, or `pacman`, directly as root or through `pkexec`. REA never invokes `sudo`.
|
||||
REA authenticates each local bridge session with a random capability token and a current-user Unix socket. This is not a sandbox and does not protect against malicious processes already running as the same macOS user. Opening an untrusted binary delegates parsing and analysis to Hopper with that user's permissions.
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
import com.atxx.jhmcp.*;
|
||||
import com.google.gson.*;
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.*;
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.*;
|
||||
import jadx.api.*;
|
||||
import jadx.core.dex.nodes.*;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
import kotlin.Unit;
|
||||
import kotlin.coroutines.EmptyCoroutineContext;
|
||||
import kotlinx.coroutines.BuildersKt;
|
||||
import kotlinx.io.CoreKt;
|
||||
import kotlinx.io.JvmCoreKt;
|
||||
import kotlinx.serialization.json.JsonObject;
|
||||
|
||||
/** REA-owned metadata boundary for the unmodified jadx-headless-mcp 0.7.1 JAR. */
|
||||
class ReaJadxBridge {
|
||||
private static final Gson JSON = new Gson();
|
||||
private final SessionHolder holder;
|
||||
private JadxSession indexedSession;
|
||||
private Map<String, List<ClassSnapshot>> classes = Map.of();
|
||||
|
||||
private record MethodSnapshot(String name, String shortId, Map<String, Object> summary) {}
|
||||
private record ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary) {}
|
||||
|
||||
private ReaJadxBridge(SessionHolder holder) { this.holder = holder; }
|
||||
|
||||
private static CallToolResult text(String value, boolean failed) {
|
||||
return new CallToolResult(List.of(new TextContent(value, null, null)), failed, null, null);
|
||||
}
|
||||
|
||||
private static CallToolResult json(Object value) { return text(JSON.toJson(value), false); }
|
||||
|
||||
private static String string(JsonObject args, String name) {
|
||||
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
|
||||
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
|
||||
throw new IllegalArgumentException(name + " must be a string");
|
||||
return value.getAsString();
|
||||
}
|
||||
|
||||
private static int integer(JsonObject args, String name, int fallback) {
|
||||
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
|
||||
if (value == null) return fallback;
|
||||
if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber())
|
||||
throw new IllegalArgumentException(name + " must be a nonnegative integer");
|
||||
try {
|
||||
int result = value.getAsBigDecimal().intValueExact();
|
||||
if (result < 0) throw new ArithmeticException();
|
||||
return result;
|
||||
} catch (ArithmeticException error) {
|
||||
throw new IllegalArgumentException(name + " must be a nonnegative integer", error);
|
||||
}
|
||||
}
|
||||
|
||||
private JadxSession session() {
|
||||
JadxSession current = holder.current();
|
||||
if (current == null) throw new IllegalArgumentException("No APK loaded");
|
||||
return current;
|
||||
}
|
||||
|
||||
private ClassSnapshot selected(JsonObject args) {
|
||||
JadxSession current = session();
|
||||
if (indexedSession != current) {
|
||||
Map<String, List<ClassSnapshot>> index = new LinkedHashMap<>();
|
||||
if (!current.getClasses().isEmpty()) {
|
||||
JadxDecompiler decompiler = current.getClasses().get(0).getClassNode().root().getDecompiler();
|
||||
// JavaClass.getMethods/getFields/getInnerClasses all load code. Raw nodes do not.
|
||||
// Snapshot every identity before code generation can rewrite aliases or members.
|
||||
for (JavaClass cls : decompiler.getClassesWithInners()) {
|
||||
ClassNode node = cls.getClassNode();
|
||||
List<MethodNode> ordered = new ArrayList<>(node.getMethods());
|
||||
ordered.sort(Comparator.comparing(MethodNode::getAlias));
|
||||
List<MethodSnapshot> methods = ordered.stream().map(method -> {
|
||||
String name = method.getAlias();
|
||||
String signature = name + "(" + method.getArgTypes().stream()
|
||||
.map(Object::toString).collect(Collectors.joining(", "))
|
||||
+ "): " + method.getReturnType().toString();
|
||||
return new MethodSnapshot(name, method.getMethodInfo().getShortId(), Map.of(
|
||||
"name", name, "signature", signature,
|
||||
"is_constructor", method.isConstructor(), "def_pos", 0));
|
||||
}).toList();
|
||||
List<Map<String, Object>> fields = node.getFields().stream()
|
||||
.sorted(Comparator.comparing(FieldNode::getAlias))
|
||||
.map(field -> Map.<String, Object>of("name", field.getAlias(),
|
||||
"type", field.getType().toString(), "def_pos", 0)).toList();
|
||||
List<String> inner = node.getInnerClasses().stream().map(ClassNode::getFullName).toList();
|
||||
Map<String, Object> summary = Map.of(
|
||||
"full_name", cls.getFullName(), "name", cls.getName(),
|
||||
"method_count", methods.size(), "field_count", fields.size(),
|
||||
"inner_class_count", inner.size(), "inner_classes", inner,
|
||||
"methods", methods.stream().map(MethodSnapshot::summary).toList(), "fields", fields);
|
||||
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>())
|
||||
.add(new ClassSnapshot(cls, methods, summary));
|
||||
}
|
||||
}
|
||||
classes = Collections.unmodifiableMap(index);
|
||||
indexedSession = current;
|
||||
}
|
||||
String name = string(args, "class_name");
|
||||
List<ClassSnapshot> candidates = classes.get(name);
|
||||
if (candidates == null) throw new IllegalArgumentException("Exact class not found: " + name);
|
||||
if (candidates.size() != 1) throw new IllegalArgumentException("Ambiguous metadata class identity: " + name
|
||||
+ "; raw identities: " + candidates.stream().map(candidate -> candidate.node().getRawName()).toList());
|
||||
return candidates.get(0);
|
||||
}
|
||||
|
||||
private List<MethodSnapshot> methods(ClassSnapshot cls, JsonObject args) {
|
||||
String name = string(args, "method_name");
|
||||
List<MethodSnapshot> result = cls.methods().stream().filter(method -> method.name().equals(name)).toList();
|
||||
if (result.isEmpty()) throw new IllegalArgumentException("Method not found: " + name);
|
||||
return result;
|
||||
}
|
||||
|
||||
private JavaMethod method(ClassSnapshot cls, MethodSnapshot selected) {
|
||||
JavaMethod result = cls.node().searchMethodByShortId(selected.shortId());
|
||||
if (result == null) throw new IllegalArgumentException("Method identity no longer available: " + selected.shortId());
|
||||
return result;
|
||||
}
|
||||
|
||||
private CallToolResult handle(String name, JsonObject args) {
|
||||
if (name.equals("rea_jvm_status")) return json(Map.of(
|
||||
"engine_reported_version", BuildInfo.VERSION,
|
||||
"max_heap_bytes", Runtime.getRuntime().maxMemory(),
|
||||
"available_processors", Runtime.getRuntime().availableProcessors(),
|
||||
"java_version", System.getProperty("java.version"),
|
||||
"metadata_scope", "parsed_members"));
|
||||
JadxSession current = session();
|
||||
if (name.equals("list_classes")) {
|
||||
List<String> names = current.getClassFqns();
|
||||
int offset = integer(args, "offset", 0);
|
||||
int limit = integer(args, "limit", 200);
|
||||
int start = Math.min(offset, names.size());
|
||||
int end = (int) Math.min((long) start + limit, names.size());
|
||||
return json(Map.of("total", names.size(), "offset", offset, "limit", limit,
|
||||
"items", names.subList(start, end)));
|
||||
}
|
||||
ClassSnapshot cls = selected(args);
|
||||
if (name.equals("get_class_summary")) return json(cls.summary());
|
||||
if (name.equals("get_method_body")) {
|
||||
List<MethodSnapshot> overloads = methods(cls, args);
|
||||
int index = integer(args, "overload_index", 0);
|
||||
if (index >= overloads.size()) throw new IllegalArgumentException("overload_index out of range: " + index);
|
||||
JavaMethod method = method(cls, overloads.get(index));
|
||||
JadxSession.SmartCode code = current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
|
||||
return json(Map.of("class_name", cls.summary().get("full_name"),
|
||||
"method_name", overloads.get(index).name(), "full_name", method.getFullName(),
|
||||
"overload_index", index, "overload_count", overloads.size(),
|
||||
"mode", code.getKind(), "fell_back", code.getFellBack(),
|
||||
"markers", code.getMarkers(), "body", code.getText()));
|
||||
}
|
||||
List<JavaNode> uses;
|
||||
String target = string(args, "class_name");
|
||||
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
|
||||
else {
|
||||
List<MethodSnapshot> overloads = methods(cls, args);
|
||||
if (overloads.size() != 1) throw new IllegalArgumentException("Method references require a unique overload");
|
||||
target += "." + overloads.get(0).name();
|
||||
uses = method(cls, overloads.get(0)).getUseIn();
|
||||
}
|
||||
// REA consumes all references without materializing source line information.
|
||||
List<Map<String, Object>> items = uses.stream().map(node -> current.describeUsage(node, false)).toList();
|
||||
return json(Map.of("target", target, "total", uses.size(), "count", items.size(),
|
||||
"resolve_line", false, "items", items));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
PrintStream protocolOut = System.out;
|
||||
System.setOut(System.err);
|
||||
SessionHolder holder = new SessionHolder(new SessionConfig(
|
||||
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL));
|
||||
ServerCapabilities capabilities = new ServerCapabilities(
|
||||
new ServerCapabilities.Tools(null), null, null, null, null, null, null, Map.of());
|
||||
Server server = new Server(new Implementation("rea-jadx-bridge", "1", null, null, null),
|
||||
new ServerOptions(capabilities), "", ignored -> Unit.INSTANCE);
|
||||
SessionToolsKt.registerSessionTools(server, holder);
|
||||
ManifestToolsKt.registerManifestTools(server, holder);
|
||||
ClassToolsKt.registerClassTools(server, holder);
|
||||
MethodToolsKt.registerMethodTools(server, holder);
|
||||
XrefToolsKt.registerXrefTools(server, holder);
|
||||
ReaJadxBridge bridge = new ReaJadxBridge(holder);
|
||||
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
|
||||
"list_classes", "get_class_summary", "get_method_body",
|
||||
"get_xrefs_to_class", "get_xrefs_to_method");
|
||||
for (String name : new ArrayList<>(server.getTools().keySet()))
|
||||
if (!consumed.contains(name)) server.removeTool(name);
|
||||
Tool diagnostics = new Tool("rea_jvm_status",
|
||||
new ToolSchema(null, new JsonObject(Map.of()), List.of(), null),
|
||||
"Observed worker JVM and metadata scope", null, null, null, null, null, null);
|
||||
server.addTool(diagnostics, (connection, request, continuation) -> bridge.handle("rea_jvm_status", request.getArguments()));
|
||||
for (String name : List.of("list_classes", "get_class_summary",
|
||||
"get_method_body", "get_xrefs_to_class", "get_xrefs_to_method")) {
|
||||
Tool tool = server.getTools().get(name).getTool();
|
||||
server.removeTool(name);
|
||||
List<String> fields = switch (name) {
|
||||
case "list_classes" -> List.of("offset", "limit");
|
||||
case "get_method_body" -> List.of("class_name", "method_name", "overload_index");
|
||||
case "get_xrefs_to_method" -> List.of("class_name", "method_name");
|
||||
default -> List.of("class_name");
|
||||
};
|
||||
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
|
||||
for (String field : fields) properties.put(field, tool.getInputSchema().getProperties().get(field));
|
||||
String description = switch (name) {
|
||||
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
|
||||
case "get_class_summary" -> "Parsed member metadata for one exact class name; no source generation.";
|
||||
case "get_method_body" -> "Source for an exact class and stable same-name overload index, with smali fallback.";
|
||||
default -> "Complete static references to an exact class or unique method; source lines are unresolved.";
|
||||
};
|
||||
Tool narrowed = new Tool(name, new ToolSchema(null, new JsonObject(properties),
|
||||
tool.getInputSchema().getRequired(), null), description, null, null, null, null, null, null);
|
||||
server.addTool(narrowed, (connection, request, continuation) -> {
|
||||
try { return bridge.handle(name, request.getArguments()); }
|
||||
catch (IllegalArgumentException error) { return text(error.getMessage(), true); }
|
||||
});
|
||||
}
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
server.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
StdioServerTransport transport = new StdioServerTransport(
|
||||
CoreKt.buffered(JvmCoreKt.asSource(System.in)),
|
||||
CoreKt.buffered(JvmCoreKt.asSink(protocolOut)));
|
||||
transport.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
ServerSession connection = null;
|
||||
try {
|
||||
connection = BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.createSession(transport, continuation));
|
||||
done.await();
|
||||
} finally {
|
||||
try {
|
||||
if (connection != null) {
|
||||
ServerSession connected = connection;
|
||||
// Unsubscribe the connection before the SDK joins its notification service.
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> connected.close(continuation));
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.close(continuation));
|
||||
} finally {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> holder.unload(continuation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,113 +0,0 @@
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Session-owned extension loading; analyzer algorithms stay behind each adapter ABI. */
|
||||
final class ReaGhidraExtensions implements AutoCloseable {
|
||||
private record Loaded(URLClassLoader loader, Object instance, String sha256) {}
|
||||
private final List<Loaded> loaded = new ArrayList<>();
|
||||
private final JsonArray reports = new JsonArray();
|
||||
|
||||
void analyze(JsonArray descriptors, Program program, TaskMonitor monitor) throws Exception {
|
||||
for (var item : descriptors) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject descriptor = item.getAsJsonObject();
|
||||
String id = descriptor.get("id").getAsString();
|
||||
String entry = descriptor.get("entry_class").getAsString();
|
||||
String digest = descriptor.get("sha256").getAsString();
|
||||
JsonObject report = new JsonObject();
|
||||
report.addProperty("id", id);
|
||||
report.addProperty("sha256", digest);
|
||||
URLClassLoader loader = null;
|
||||
try {
|
||||
if (!id.equals("nativeaot") || !entry.equals("rea.extensions.nativeaot.NativeAotExtension") ||
|
||||
descriptor.get("integration_api").getAsInt() != 1)
|
||||
throw new IllegalArgumentException("Unregistered Ghidra extension ABI: " + id);
|
||||
Path path = Path.of(descriptor.get("path").getAsString());
|
||||
if (!path.isAbsolute() || !Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS) || Files.size(path) > 8 * 1024 * 1024)
|
||||
throw new IllegalArgumentException("Invalid private extension artifact: " + path);
|
||||
String measured = java.util.HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(path)));
|
||||
if (!measured.equals(digest)) throw new IllegalArgumentException("Private extension digest mismatch: " + id);
|
||||
loader = new URLClassLoader(new java.net.URL[] { path.toUri().toURL() }, getClass().getClassLoader());
|
||||
Class<?> type = Class.forName(entry, true, loader);
|
||||
Object instance = type.getConstructor().newInstance();
|
||||
Object response = type.getMethod("analyze", Program.class, TaskMonitor.class).invoke(instance, program, monitor);
|
||||
if (!(response instanceof JsonObject result)) throw new IllegalArgumentException("Invalid Ghidra extension result: " + id);
|
||||
report.addProperty("status", result.get("status").getAsString());
|
||||
report.add("reason", result.get("reason").deepCopy());
|
||||
report.add("result", result);
|
||||
loaded.add(new Loaded(loader, instance, digest));
|
||||
loader = null;
|
||||
} catch (InvocationTargetException ex) {
|
||||
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
||||
failure(report, ex.getCause());
|
||||
} catch (Exception | LinkageError ex) {
|
||||
failure(report, ex);
|
||||
} finally {
|
||||
if (loader != null) loader.close();
|
||||
}
|
||||
reports.add(report);
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray reports() { return reports.deepCopy(); }
|
||||
|
||||
JsonArray inspectImage() throws Exception {
|
||||
JsonArray summaries = new JsonArray();
|
||||
for (Loaded extension : loaded) {
|
||||
Object response = extension.instance.getClass().getMethod("inspectImage").invoke(extension.instance);
|
||||
if (!(response instanceof JsonObject summary)) throw new IllegalArgumentException("Invalid extension image summary");
|
||||
summary.addProperty("analysis_artifact_sha256", extension.sha256);
|
||||
summaries.add(summary);
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
JsonObject inspectDataType(Program program, DataType type, Address address, TaskMonitor monitor) throws Exception {
|
||||
for (Loaded extension : loaded) {
|
||||
try {
|
||||
Object response = extension.instance.getClass().getMethod("inspectDataType", Program.class, DataType.class, Address.class, TaskMonitor.class)
|
||||
.invoke(extension.instance, program, type, address, monitor);
|
||||
if (response instanceof JsonObject result) return result;
|
||||
if (response != null) throw new IllegalArgumentException("Invalid extension type metadata result");
|
||||
} catch (InvocationTargetException ex) {
|
||||
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
||||
throw new IllegalStateException("Ghidra extension type inspection failed: " + ex.getCause(), ex.getCause());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void failure(JsonObject report, Throwable ex) {
|
||||
String reason = "Extension loading/analysis failed: " + ex;
|
||||
if (ex.getCause() != null) reason += "; caused by " + ex.getCause();
|
||||
report.addProperty("status", "failed");
|
||||
report.addProperty("reason", reason);
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("loader_failure", reason);
|
||||
report.add("result", result);
|
||||
}
|
||||
|
||||
@Override public void close() throws Exception {
|
||||
Exception failure = null;
|
||||
for (Loaded extension : loaded) {
|
||||
try { extension.loader.close(); }
|
||||
catch (Exception ex) { if (failure == null) failure = ex; else failure.addSuppressed(ex); }
|
||||
}
|
||||
loaded.clear();
|
||||
if (failure != null) throw failure;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/** Seed the declared DOS COM analysis context before default auto-analysis. */
|
||||
public final class ReaGhidraPrepareCom extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (!currentProgram.getLanguageID().getIdAsString().equals("x86:LE:16:Real Mode") ||
|
||||
!currentProgram.getExecutableFormat().equals("Raw Binary")) {
|
||||
throw new IllegalArgumentException("DOS COM preparation requires raw x86 real-mode import");
|
||||
}
|
||||
Address entry = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress("1000:0100");
|
||||
Address start = currentProgram.getMinAddress();
|
||||
Address end = currentProgram.getMaxAddress();
|
||||
if (!entry.equals(start) || end.subtract(start) + 1 > 0xff00) {
|
||||
throw new IllegalArgumentException("DOS COM import must map 1..65280 bytes at 1000:0100");
|
||||
}
|
||||
for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
|
||||
monitor.checkCancelled();
|
||||
currentProgram.getProgramContext().setValue(
|
||||
currentProgram.getRegister(name), start, end, BigInteger.valueOf(0x1000)
|
||||
);
|
||||
}
|
||||
addEntryPoint(entry);
|
||||
if (!disassemble(entry) || createFunction(entry, "entry") == null) {
|
||||
throw new IllegalStateException("DOS COM entry cannot be decoded as a function");
|
||||
}
|
||||
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA DOS COM prepared", true);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import java.util.LinkedHashSet;
|
||||
import nativeaot.rtr.ReadyToRunDirectory;
|
||||
import nativeaot.rtr.ReadyToRunSection;
|
||||
import nativeaot.rtr.SignatureReadyToRunLocator;
|
||||
import nativeaot.rtr.SymbolReadyToRunLocator;
|
||||
|
||||
/** Validate the producer's NativeAOT row layout before database mutation. */
|
||||
final class NativeAotDirectory {
|
||||
record Located(Address address, ReadyToRunDirectory directory, String discovery) {}
|
||||
|
||||
static Located locate(Program program, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
Address[] symbols = SymbolReadyToRunLocator.instance.locateModules(program, monitor, log);
|
||||
String discovery = symbols.length == 0 ? "signature-heuristic" : "symbol";
|
||||
Address[] found = symbols.length == 0
|
||||
? SignatureReadyToRunLocator.instance.locateModules(program, monitor, log) : symbols;
|
||||
var candidates = new LinkedHashSet<Address>();
|
||||
for (Address address : found) if (address.getOffset() != 0) candidates.add(address);
|
||||
if (candidates.isEmpty()) return null;
|
||||
if (candidates.size() != 1)
|
||||
throw new UnsupportedOperationException("Ambiguous NativeAOT directory candidates: " + candidates);
|
||||
Address address = candidates.iterator().next();
|
||||
var memory = program.getMemory();
|
||||
byte[] header = new byte[16];
|
||||
if (memory.getBytes(address, header) != header.length)
|
||||
throw new IllegalArgumentException("Incomplete NativeAOT header at " + address);
|
||||
int count = Short.toUnsignedInt(memory.getShort(address.add(12)));
|
||||
if (count < 1 || count > 80 || Byte.toUnsignedInt(header[14]) != 24 || header[15] != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT row layout at " + address +
|
||||
": sections=" + count + ", entry_size=" + Byte.toUnsignedInt(header[14]) + ", entry_type=" + Byte.toUnsignedInt(header[15]));
|
||||
byte[] rows = new byte[count * 24];
|
||||
if (memory.getBytes(address.add(16), rows) != rows.length)
|
||||
throw new IllegalArgumentException("Incomplete NativeAOT directory rows at " + address);
|
||||
ReadyToRunDirectory directory = ReadyToRunDirectory.readAtAddress(program, address);
|
||||
if (directory.getMajorVersion() != 9 || directory.getMinorVersion() != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT RTR format " +
|
||||
directory.getMajorVersion() + "." + directory.getMinorVersion() + " at " + address + "; verified format is 9.1 (.NET 8.0.22 Linux x64 fixture).");
|
||||
var types = new LinkedHashSet<Integer>();
|
||||
for (ReadyToRunSection section : directory.getSections()) {
|
||||
monitor.checkCancelled();
|
||||
if (!types.add(section.getType())) throw new IllegalArgumentException("Duplicate NativeAOT section " + section.getType() + " at " + address);
|
||||
// ModuleInfoRow flags bit 0 declares an end pointer. ThreadStaticIndex
|
||||
// (204) is a single-address entry with flags=0 and an absent end.
|
||||
if (section.getFlags() != 0 && section.getFlags() != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT section flags " + section.getFlags() + " for section " + section.getType());
|
||||
boolean hasEnd = section.getFlags() == 1;
|
||||
if (!hasEnd && section.getEnd() != 0)
|
||||
throw new IllegalArgumentException("NativeAOT single-address section " + section.getType() + " reports an unexpected end");
|
||||
if (hasEnd && Long.compareUnsigned(section.getStart(), section.getEnd()) > 0)
|
||||
throw new IllegalArgumentException("Reversed NativeAOT section " + section.getType() + " at " + address);
|
||||
if (section.getStart() != 0 && (!hasEnd || section.getStart() != section.getEnd()) &&
|
||||
(!memory.contains(address.getNewAddress(section.getStart())) ||
|
||||
(hasEnd && !memory.contains(address.getNewAddress(section.getEnd() - 1)))))
|
||||
throw new IllegalArgumentException("Unmapped NativeAOT section " + section.getType() + " at " + address);
|
||||
}
|
||||
if (directory.getSectionByType(ReadyToRunSection.DEHYDRATED_DATA) == null)
|
||||
throw new UnsupportedOperationException("NativeAOT directory at " + address + " has no DEHYDRATED_DATA; non-dehydrated layouts are not verified.");
|
||||
for (int type : new int[] { ReadyToRunSection.DEHYDRATED_DATA, ReadyToRunSection.FROZEN_OBJECT_REGION }) {
|
||||
ReadyToRunSection section = directory.getSectionByType(type);
|
||||
if (section != null && section.getFlags() != 1)
|
||||
throw new IllegalArgumentException("NativeAOT section " + type + " requires an end pointer");
|
||||
}
|
||||
return new Located(address, directory, discovery);
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.FrozenObjectAnnotator;
|
||||
import nativeaot.objectmodel.MethodTableCrawler;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
import nativeaot.rehydration.MetadataRehydratorNet80;
|
||||
import nativeaot.rtr.ReadyToRunSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/** Optional headless facade over the unchanged upstream analysis primitives. */
|
||||
public final class NativeAotExtension {
|
||||
private MethodTableManager manager;
|
||||
private NativeAotDirectory.Located located;
|
||||
private JsonObject report;
|
||||
private final List<String> diagnostics = new ArrayList<>();
|
||||
private JsonObject derivedRange;
|
||||
|
||||
public JsonObject analyze(Program program, TaskMonitor monitor) throws Exception {
|
||||
report = new JsonObject();
|
||||
report.addProperty("id", "nativeaot");
|
||||
report.addProperty("integration_api", 1);
|
||||
report.addProperty("source_revision", "effeb734fc570c32650f88b159608979dc7b423e");
|
||||
report.addProperty("source_revision_authority", "build-reported-unattested");
|
||||
report.addProperty("status", "not_applicable");
|
||||
report.addProperty("reason", "No NativeAOT directory with the supported row layout was found.");
|
||||
report.addProperty("method_tables", 0);
|
||||
if (!program.getLanguageID().getIdAsString().startsWith("x86:LE:64"))
|
||||
return failure("unsupported", "NativeAOT adapter supports x86-64 only.");
|
||||
MessageLog log = new MessageLog();
|
||||
int transaction = program.startTransaction("REA NativeAOT metadata recovery");
|
||||
boolean commit = false;
|
||||
String stage = "directory-discovery";
|
||||
try {
|
||||
located = NativeAotDirectory.locate(program, monitor, log);
|
||||
if (located == null) return finish(log);
|
||||
report.addProperty("header_address", address(located.address()));
|
||||
report.addProperty("discovery", located.discovery());
|
||||
report.addProperty("format_major", located.directory().getMajorVersion());
|
||||
report.addProperty("format_minor", located.directory().getMinorVersion());
|
||||
stage = "metadata-rehydration";
|
||||
var section = located.directory().getSectionByType(ReadyToRunSection.DEHYDRATED_DATA);
|
||||
Address start = located.address().getNewAddress(section.getStart());
|
||||
Address end = located.address().getNewAddress(section.getEnd());
|
||||
if (start.compareTo(end) >= 0) throw new IllegalArgumentException("Empty dehydrated section at " + start);
|
||||
var scan = new MetadataRehydratorNet80().rehydrate(program, new AddressSet(start, end).getFirstRange(), monitor, log);
|
||||
// Upstream reports an inclusive end at base + written length.
|
||||
long writtenLength = scan.getScanningRange().getLength() - 1;
|
||||
derivedRange = new JsonObject();
|
||||
derivedRange.addProperty("address", address(scan.getScanningRange().getMinAddress()));
|
||||
derivedRange.addProperty("size_bytes", writtenLength);
|
||||
derivedRange.addProperty("sha256", hashMemory(program, scan.getScanningRange().getMinAddress(), writtenLength, monitor));
|
||||
derivedRange.add("file_offset", JsonNull.INSTANCE);
|
||||
stage = "method-table-recovery";
|
||||
manager = MethodTableManager.createForDirectory(program, located.directory());
|
||||
var conventions = NativeAotMethodConventions.capture(program, monitor);
|
||||
new MethodTableCrawler(manager, program, scan).analyze(monitor, log);
|
||||
stage = "calling-convention-preservation";
|
||||
NativeAotMethodConventions.restore(program, manager, conventions, monitor);
|
||||
log.appendMsg("REA", "Preserved pre-recovery calling conventions; new functions use the loaded compiler specification default. Original method signatures remain unknown.");
|
||||
if (manager.getObjectMT() == null || manager.getMethodTableCount() == 0)
|
||||
throw new UnsupportedOperationException("No unambiguous System.Object method table was recovered at header " + located.address());
|
||||
stage = "frozen-object-annotation";
|
||||
if (manager.getStringMT() == null || scan.getPointerLocations().length == 0) {
|
||||
diagnostics.add("Frozen-object annotation unavailable: System.String table or pointer locations were not recovered.");
|
||||
} else {
|
||||
new FrozenObjectAnnotator(program, manager).analyze(located.directory(), scan.getPointerLocations(), monitor, log);
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
report.addProperty("method_tables", manager.getMethodTableCount());
|
||||
JsonObject coverage = auditFrozenObjects(program, scan.getPointerLocations(), monitor);
|
||||
report.add("coverage", coverage);
|
||||
if (coverage.get("frozen_objects_annotated").getAsInt() != coverage.get("frozen_object_candidates").getAsInt())
|
||||
diagnostics.add("Some frozen-object candidates lack committed instance annotations; inspect coverage counts and Ghidra diagnostics.");
|
||||
report.addProperty("status", diagnostics.isEmpty() ? "complete" : "partial");
|
||||
report.add("reason", JsonNull.INSTANCE);
|
||||
report.add("derived_memory", derivedRange.deepCopy());
|
||||
JsonArray tables = new JsonArray();
|
||||
for (var mt : manager.getMethodTables()) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject identity = new JsonObject();
|
||||
identity.addProperty("address", address(mt.getAddress()));
|
||||
identity.addProperty("type", mt.getMTType().getPathName());
|
||||
tables.add(identity);
|
||||
}
|
||||
report.add("types", tables);
|
||||
commit = true;
|
||||
return finish(log);
|
||||
} catch (CancelledException ex) {
|
||||
manager = null;
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
manager = null;
|
||||
diagnostics.add(stage + ": " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
|
||||
report.addProperty("status", ex instanceof UnsupportedOperationException ? "unsupported" : "failed");
|
||||
report.addProperty("reason", stage + ": " + ex.getMessage());
|
||||
return finish(log);
|
||||
} finally {
|
||||
program.endTransaction(transaction, commit);
|
||||
}
|
||||
}
|
||||
|
||||
/** Inline discovery for the existing loaded-image inspection. */
|
||||
public JsonObject inspectImage() {
|
||||
JsonObject summary = new JsonObject();
|
||||
summary.addProperty("format", "dotnet-nativeaot");
|
||||
for (String key : new String[] { "status", "reason", "header_address", "discovery", "format_major", "format_minor", "method_tables", "derived_memory", "coverage", "diagnostics" })
|
||||
summary.add(key, report.has(key) ? report.get(key).deepCopy() : JsonNull.INSTANCE);
|
||||
summary.add("types", report.has("types") ? report.get("types").deepCopy() : new JsonArray());
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add("Recovery covers the reported metadata candidates; it does not reconstruct original C# source, custom field layouts, every runtime object, or execution behavior.");
|
||||
limitations.add("Method prototypes and parameter names remain Ghidra inferences; verify them against instructions and call sites. Pre-recovery conventions are retained rather than universally forcing __thiscall.");
|
||||
limitations.add("NativeAOT type identities and relationships are inferred; generated names are not original names. Derived memory has no original file offsets.");
|
||||
summary.add("limitations", limitations);
|
||||
return summary;
|
||||
}
|
||||
|
||||
/** Read recovered relationships without restoring or mutating database state. */
|
||||
public JsonObject inspectDataType(Program program, DataType type, Address selected, TaskMonitor monitor) throws Exception {
|
||||
if (manager == null) return null;
|
||||
return NativeAotTypeMetadata.describe(program, manager, located, type, report, derivedRange, monitor);
|
||||
}
|
||||
|
||||
private JsonObject auditFrozenObjects(Program program, Address[] pointerLocations, TaskMonitor monitor) throws Exception {
|
||||
JsonObject coverage = new JsonObject();
|
||||
int candidates = 0, annotated = 0;
|
||||
var section = located.directory().getSectionByType(ReadyToRunSection.FROZEN_OBJECT_REGION);
|
||||
if (section != null) for (Address location : section.getPointersInSection(pointerLocations)) {
|
||||
monitor.checkCancelled();
|
||||
var table = manager.getMethodTable(program.getMemory().getLong(location));
|
||||
if (table == null) continue;
|
||||
candidates++;
|
||||
var expected = table.getInstanceType();
|
||||
var actual = program.getListing().getDataAt(location);
|
||||
if (expected != null && actual != null && actual.getDataType().getPathName().equals(expected.getPathName())) annotated++;
|
||||
}
|
||||
coverage.addProperty("frozen_object_candidates", candidates);
|
||||
coverage.addProperty("frozen_objects_annotated", annotated);
|
||||
coverage.addProperty("basis", "rehydrated-pointer-candidates-and-committed-instance-types");
|
||||
return coverage;
|
||||
}
|
||||
|
||||
private JsonObject finish(MessageLog log) {
|
||||
if (!log.toString().isBlank()) diagnostics.add(log.toString());
|
||||
JsonArray messages = new JsonArray();
|
||||
for (String message : diagnostics) messages.add(message);
|
||||
report.add("diagnostics", messages);
|
||||
return report;
|
||||
}
|
||||
|
||||
private JsonObject failure(String status, String reason) {
|
||||
report.addProperty("status", status);
|
||||
report.addProperty("reason", reason);
|
||||
return finish(new MessageLog());
|
||||
}
|
||||
|
||||
static String address(Address value) { return "0x" + Long.toUnsignedString(value.getOffset(), 16); }
|
||||
|
||||
private static String hashMemory(Program program, Address start, long length, TaskMonitor monitor) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] buffer = new byte[65536];
|
||||
for (long offset = 0; offset < length;) {
|
||||
monitor.checkCancelled();
|
||||
int count = (int)Math.min(buffer.length, length - offset);
|
||||
if (program.getMemory().getBytes(start.add(offset), buffer, 0, count) != count)
|
||||
throw new IllegalStateException("Incomplete derived-memory read at " + start.add(offset));
|
||||
digest.update(buffer, 0, count);
|
||||
offset += count;
|
||||
}
|
||||
return java.util.HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/** Keep upstream's unconditional __thiscall assignment out of x64 ABI evidence. */
|
||||
final class NativeAotMethodConventions {
|
||||
static Map<Address, String> capture(Program program, TaskMonitor monitor) throws Exception {
|
||||
Map<Address, String> result = new HashMap<>();
|
||||
var functions = program.getFunctionManager().getFunctions(true);
|
||||
while (functions.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
var function = functions.next();
|
||||
result.put(function.getEntryPoint(), function.getCallingConventionName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void restore(Program program, MethodTableManager manager, Map<Address, String> previous, TaskMonitor monitor) throws Exception {
|
||||
var visited = new HashSet<Address>();
|
||||
String defaultConvention = program.getCompilerSpec().getDefaultCallingConvention().getName();
|
||||
for (var table : manager.getMethodTables()) for (long target : table.getVTable()) {
|
||||
monitor.checkCancelled();
|
||||
Address address = table.getAddress().getNewAddress(target);
|
||||
if (!visited.add(address)) continue;
|
||||
var function = program.getFunctionManager().getFunctionAt(address);
|
||||
if (function == null) continue;
|
||||
String convention = previous.containsKey(address) ? previous.get(address) : defaultConvention;
|
||||
if (!java.util.Objects.equals(function.getCallingConventionName(), convention))
|
||||
function.setCallingConvention(convention);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.MethodTable;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
|
||||
/** Translate a selected recovered type into provider-neutral metadata evidence. */
|
||||
final class NativeAotTypeMetadata {
|
||||
static JsonObject describe(Program program, MethodTableManager manager, NativeAotDirectory.Located located,
|
||||
DataType type, JsonObject report, JsonObject derivedRange, TaskMonitor monitor) throws Exception {
|
||||
for (MethodTable table : manager.getMethodTables()) {
|
||||
monitor.checkCancelled();
|
||||
boolean sameType = type.getPathName().equals(table.getMTType().getPathName()) ||
|
||||
(table.getInstanceType() != null && type.getPathName().equals(table.getInstanceType().getPathName()));
|
||||
if (!sameType) continue;
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("format", "dotnet-nativeaot");
|
||||
result.addProperty("status", report.get("status").getAsString());
|
||||
result.addProperty("header_address", NativeAotExtension.address(located.address()));
|
||||
result.addProperty("format_major", located.directory().getMajorVersion());
|
||||
result.addProperty("format_minor", located.directory().getMinorVersion());
|
||||
result.addProperty("method_table_address", NativeAotExtension.address(table.getAddress()));
|
||||
result.addProperty("name_origin", table == manager.getObjectMT() || table == manager.getStringMT() ? "inferred" : "generated");
|
||||
result.add("original_name", JsonNull.INSTANCE);
|
||||
result.addProperty("base_size_bytes", table.getBaseSize());
|
||||
result.add("related_type", identity(manager, table.getRelatedTypeAddress()));
|
||||
JsonArray interfaces = new JsonArray();
|
||||
for (long value : table.getInterfaceAddresses()) if (value != 0) interfaces.add(identity(manager, value));
|
||||
result.add("interfaces", interfaces);
|
||||
JsonArray slots = new JsonArray();
|
||||
for (int slot = 0; slot < table.getVTableSlotCount(); slot++) {
|
||||
monitor.checkCancelled();
|
||||
long value = table.getVTableSlot(slot);
|
||||
JsonObject target = new JsonObject();
|
||||
target.addProperty("slot", slot);
|
||||
target.addProperty("slot_address", NativeAotExtension.address(table.getAddress().add(24 + slot * 8L)));
|
||||
target.add("target_address", value == 0 ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive("0x" + Long.toUnsignedString(value, 16)));
|
||||
var function = value == 0 ? null : program.getFunctionManager().getFunctionAt(table.getAddress().getNewAddress(value));
|
||||
target.add("procedure_name", function == null ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive(function.getName(true)));
|
||||
target.addProperty("basis", "method-table-pointer");
|
||||
slots.add(target);
|
||||
}
|
||||
result.add("virtual_slots", slots);
|
||||
result.add("derived_memory", derivedRange.deepCopy());
|
||||
result.add("diagnostics", report.get("diagnostics").deepCopy());
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add("Type identities and relationships are recovered by upstream heuristics. Original source names and custom field layouts are not recovered.");
|
||||
limitations.add("Method prototypes are inferred, not original signatures. REA preserves pre-recovery calling conventions and the loaded compiler default for new functions.");
|
||||
limitations.add("Rehydrated metadata bytes are derived analysis-memory content; their original file offsets and runtime behavior remain unknown.");
|
||||
limitations.add("Verified workflow: .NET 8.0.22 NativeAOT RTR 9.1, Linux x64 ELF / Windows x64 PE on Linux; other runtime layouts and hosts are unverified.");
|
||||
result.add("limitations", limitations);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static com.google.gson.JsonElement identity(MethodTableManager manager, long value) throws Exception {
|
||||
if (value == 0) return JsonNull.INSTANCE;
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("address", "0x" + Long.toUnsignedString(value, 16));
|
||||
MethodTable table = manager.getMethodTable(value);
|
||||
result.add("type", table == null ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive(table.getMTType().getPathName()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+226
-491
@@ -9,156 +9,18 @@ import json
|
||||
import hmac
|
||||
import os
|
||||
import re
|
||||
import sre_parse
|
||||
import socket
|
||||
from typing import Any, Optional, Protocol, Sequence
|
||||
|
||||
MAX_LINE_BYTES = 10 * 1024 * 1024
|
||||
BAD_ADDRESSES = (-1, 0xFFFFFFFFFFFFFFFF, None)
|
||||
_selected_document = None
|
||||
_rea_session_document = None
|
||||
_search_inventory_cache = {}
|
||||
_pseudocode_cache = {}
|
||||
_hopper_api = None
|
||||
|
||||
|
||||
class HopperDocument(Protocol):
|
||||
"""Typed minimum used by the provider-neutral Hopper API facade."""
|
||||
|
||||
def getDocumentName(self) -> str: ...
|
||||
|
||||
def getExecutableFilePath(self) -> Optional[str]: ...
|
||||
|
||||
def getDatabaseFilePath(self) -> Optional[str]: ...
|
||||
|
||||
def backgroundProcessActive(self) -> bool: ...
|
||||
|
||||
|
||||
class HopperDocumentProvider(Protocol):
|
||||
"""Hopper's injected global document provider."""
|
||||
|
||||
@classmethod
|
||||
def getAllDocuments(cls) -> Sequence[HopperDocument]: ...
|
||||
|
||||
@classmethod
|
||||
def getCurrentDocument(cls) -> Optional[HopperDocument]: ...
|
||||
|
||||
|
||||
class HopperApiFacade:
|
||||
"""Small injected boundary around Hopper globals for standalone tests."""
|
||||
|
||||
def __init__(self, document_provider: HopperDocumentProvider):
|
||||
self._document_provider = document_provider
|
||||
|
||||
def documents(self) -> Sequence[HopperDocument]:
|
||||
return self._document_provider.getAllDocuments()
|
||||
|
||||
def current_document(self) -> Optional[HopperDocument]:
|
||||
return self._document_provider.getCurrentDocument()
|
||||
|
||||
def require_analysis_complete(
|
||||
self, document: HopperDocument, operation: str
|
||||
) -> None:
|
||||
if document.backgroundProcessActive():
|
||||
raise CapabilityUnavailableError(
|
||||
"%s requires completed Hopper background analysis" % operation
|
||||
)
|
||||
|
||||
|
||||
def _configure_hopper_api(document_provider):
|
||||
"""Inject Hopper's document provider or a standalone test facade."""
|
||||
global _hopper_api, _rea_session_document
|
||||
_hopper_api = HopperApiFacade(document_provider)
|
||||
_rea_session_document = None
|
||||
|
||||
|
||||
def _api():
|
||||
"""Resolve Hopper globals lazily so importing this module needs no Hopper."""
|
||||
global _hopper_api
|
||||
if _hopper_api is not None:
|
||||
return _hopper_api
|
||||
document_provider = globals().get("Document")
|
||||
if document_provider is None:
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper Document API is unavailable outside the Hopper runtime"
|
||||
)
|
||||
_hopper_api = HopperApiFacade(document_provider)
|
||||
return _hopper_api
|
||||
|
||||
|
||||
def _session_document():
|
||||
"""Return the exact document bound to this authenticated REA session."""
|
||||
global _rea_session_document
|
||||
documents = _api().documents()
|
||||
if _rea_session_document is not None:
|
||||
return (
|
||||
_rea_session_document
|
||||
if any(document is _rea_session_document for document in documents)
|
||||
else None
|
||||
)
|
||||
return _bind_session_document(documents)
|
||||
|
||||
|
||||
def _bind_session_document(documents=None):
|
||||
"""Bind this bridge to Hopper's current document for its target."""
|
||||
global _rea_session_document
|
||||
api = _api()
|
||||
if documents is None:
|
||||
documents = api.documents()
|
||||
target = os.path.realpath(REA_TARGET_PATH)
|
||||
|
||||
def matches_target(document):
|
||||
paths = (document.getExecutableFilePath(), document.getDatabaseFilePath())
|
||||
return any(path and os.path.realpath(path) == target for path in paths)
|
||||
|
||||
current = api.current_document()
|
||||
if current is not None and matches_target(current):
|
||||
_rea_session_document = current
|
||||
return current
|
||||
matching = [document for document in documents if matches_target(document)]
|
||||
if len(matching) == 1:
|
||||
_rea_session_document = matching[0]
|
||||
return matching[0]
|
||||
if len(matching) > 1:
|
||||
raise CapabilityUnavailableError(
|
||||
"The REA session document is ambiguous; Hopper did not identify the launched document"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
class CapabilityUnavailableError(Exception):
|
||||
"""The active Hopper build lacks one admitted public API operation."""
|
||||
|
||||
|
||||
class InvalidRequestError(Exception):
|
||||
"""Caller input failed bridge request or operation validation."""
|
||||
|
||||
|
||||
EXHAUSTIVE_ANALYSIS_METHODS = frozenset(
|
||||
(
|
||||
"analyze_function",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
"list_strings",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
)
|
||||
)
|
||||
MAX_SEARCH_PATTERN_LENGTH = 256
|
||||
MAX_SEARCH_VALUE_LENGTH = 4096
|
||||
|
||||
|
||||
def _hex(value):
|
||||
if not isinstance(value, int) or isinstance(value, bool):
|
||||
try:
|
||||
value = int(str(value), 16)
|
||||
except ValueError as error:
|
||||
raise ValueError("Hopper returned a non-hexadecimal address") from error
|
||||
return "0x%x" % value
|
||||
|
||||
|
||||
@@ -176,28 +38,19 @@ def _json_safe(value):
|
||||
def _document(name=None):
|
||||
"""Resolve an explicit or session-selected document without changing Hopper UI."""
|
||||
global _selected_document
|
||||
api = _api()
|
||||
documents = api.documents()
|
||||
documents = Document.getAllDocuments()
|
||||
if name is not None:
|
||||
matches = [candidate for candidate in documents if candidate.getDocumentName() == name]
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
if len(matches) > 1:
|
||||
current = api.current_document()
|
||||
if current is not None and current.getDocumentName() == name:
|
||||
return current
|
||||
raise InvalidRequestError("Hopper document name is ambiguous")
|
||||
raise InvalidRequestError("Unknown Hopper document")
|
||||
for candidate in documents:
|
||||
if candidate.getDocumentName() == name:
|
||||
return candidate
|
||||
raise ValueError("Unknown Hopper document")
|
||||
if _selected_document is not None:
|
||||
for candidate in documents:
|
||||
if candidate.getDocumentName() == _selected_document:
|
||||
return candidate
|
||||
session_document = _session_document()
|
||||
if session_document is not None:
|
||||
return session_document
|
||||
current = api.current_document()
|
||||
current = Document.getCurrentDocument()
|
||||
if current is None:
|
||||
raise CapabilityUnavailableError("No Hopper document is loaded")
|
||||
raise ValueError("No Hopper document is loaded")
|
||||
return current
|
||||
|
||||
|
||||
@@ -206,20 +59,20 @@ def _address(document, value=None):
|
||||
if value is None:
|
||||
return document.getCurrentAddress()
|
||||
if not isinstance(value, str):
|
||||
raise InvalidRequestError("Address must be a string")
|
||||
raise ValueError("Address must be a string")
|
||||
try:
|
||||
return int(value, 16)
|
||||
except ValueError:
|
||||
result = document.getAddressForName(value)
|
||||
if result in BAD_ADDRESSES:
|
||||
raise InvalidRequestError("Unknown Hopper address or name")
|
||||
raise ValueError("Unknown Hopper address or name")
|
||||
return result
|
||||
|
||||
|
||||
def _segment(document, address):
|
||||
result = document.getSegmentAtAddress(address)
|
||||
if result is None:
|
||||
raise InvalidRequestError("Address is outside every segment")
|
||||
raise ValueError("Address is outside every segment")
|
||||
return result
|
||||
|
||||
|
||||
@@ -227,7 +80,7 @@ def _procedure(document, value=None):
|
||||
address = document.getCurrentAddress() if value is None else _address(document, value)
|
||||
result = _segment(document, address).getProcedureAtAddress(address)
|
||||
if result is None:
|
||||
raise InvalidRequestError("No procedure exists at the requested address")
|
||||
raise ValueError("No procedure exists at the requested address")
|
||||
return result
|
||||
|
||||
|
||||
@@ -237,15 +90,7 @@ def _procedure_name(procedure):
|
||||
|
||||
|
||||
def _procedure_identity(procedure):
|
||||
return {
|
||||
"address": _hex(procedure.getEntryPoint()),
|
||||
"name": _procedure_name(procedure),
|
||||
"classification": None,
|
||||
"body": {
|
||||
"available": False,
|
||||
"reason": "Hopper's public Python API does not expose complete function body ranges",
|
||||
},
|
||||
}
|
||||
return {"address": _hex(procedure.getEntryPoint()), "name": _procedure_name(procedure)}
|
||||
|
||||
|
||||
def _procedure_locals(procedure):
|
||||
@@ -267,14 +112,18 @@ def _containing_procedure(document, address):
|
||||
return (procedure, None) if procedure is not None else (None, "not_in_procedure")
|
||||
|
||||
|
||||
def _instruction_addresses(procedure):
|
||||
def _instruction_addresses(procedure, limit):
|
||||
result = []
|
||||
seen = set()
|
||||
segment = procedure.getSegment()
|
||||
truncated = False
|
||||
for block in procedure.basicBlockIterator():
|
||||
address = block.getStartingAddress()
|
||||
end = block.getEndingAddress()
|
||||
while address < end and address not in seen:
|
||||
if len(result) >= limit:
|
||||
truncated = True
|
||||
return result, truncated
|
||||
seen.add(address)
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
@@ -284,15 +133,24 @@ def _instruction_addresses(procedure):
|
||||
if length <= 0:
|
||||
break
|
||||
address += length
|
||||
return result
|
||||
return result, truncated
|
||||
|
||||
|
||||
def _procedure_references(document, params):
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
direction = params.get("direction", "outgoing")
|
||||
offset = params.get("offset", 0)
|
||||
limit = params.get("limit", 100)
|
||||
max_instructions = params.get("max_instructions", 500)
|
||||
if direction not in ("incoming", "outgoing"):
|
||||
raise InvalidRequestError("direction must be incoming or outgoing")
|
||||
addresses = _instruction_addresses(procedure)
|
||||
raise ValueError("direction must be incoming or outgoing")
|
||||
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
||||
raise ValueError("offset must be a non-negative integer")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
if not isinstance(max_instructions, int) or isinstance(max_instructions, bool) or max_instructions < 1 or max_instructions > 5000:
|
||||
raise ValueError("max_instructions must be an integer between 1 and 5000")
|
||||
addresses, scan_truncated = _instruction_addresses(procedure, max_instructions)
|
||||
edges = set()
|
||||
for address in addresses:
|
||||
segment = _segment(document, address)
|
||||
@@ -301,7 +159,8 @@ def _procedure_references(document, params):
|
||||
edges.add((address, reference) if direction == "outgoing" else (reference, address))
|
||||
ordered = sorted(edges)
|
||||
items = []
|
||||
for source, target in ordered:
|
||||
selected = ordered[offset:offset + limit]
|
||||
for source, target in selected:
|
||||
source_procedure, _ = _containing_procedure(document, source)
|
||||
target_procedure, _ = _containing_procedure(document, target)
|
||||
items.append({
|
||||
@@ -311,15 +170,13 @@ def _procedure_references(document, params):
|
||||
"target_procedure": _procedure_identity(target_procedure) if target_procedure is not None else None,
|
||||
"kind": _unavailable("Hopper's public Python API does not classify reference kinds"),
|
||||
})
|
||||
next_offset = offset + len(selected)
|
||||
has_more = next_offset < len(ordered)
|
||||
truncated = scan_truncated or has_more
|
||||
return {
|
||||
"procedure": _procedure_identity(procedure), "direction": direction,
|
||||
"reference_kinds_available": False,
|
||||
# The public Hopper API returns observed references but does not expose
|
||||
# enough flow metadata to enumerate calls with no resolved target.
|
||||
# Keep the required list field explicit; the provider records this as
|
||||
# unknown coverage rather than claiming the list is exhaustive.
|
||||
"unresolved_calls": [],
|
||||
"references": items,
|
||||
"references": {"items": items, "total": None if scan_truncated else len(ordered), "returned": len(items), "truncated": truncated, "next_offset": next_offset if has_more and not scan_truncated else None},
|
||||
"instructions_scanned": len(addresses), "instruction_scan_truncated": scan_truncated,
|
||||
}
|
||||
|
||||
|
||||
@@ -348,73 +205,116 @@ def _invalidate_search_inventory(document):
|
||||
del _search_inventory_cache[key]
|
||||
|
||||
|
||||
def _invalidate_pseudocode(document):
|
||||
"""Discard cached decompilation after an analysis annotation changes."""
|
||||
document_id = id(document)
|
||||
for key in list(_pseudocode_cache):
|
||||
if key[0] == document_id:
|
||||
del _pseudocode_cache[key]
|
||||
|
||||
|
||||
def _pseudocode(document, procedure):
|
||||
"""Reuse one provider decompilation for repeated bounded projections."""
|
||||
key = (id(document), procedure.getEntryPoint())
|
||||
if key not in _pseudocode_cache:
|
||||
_pseudocode_cache[key] = procedure.decompile()
|
||||
return _pseudocode_cache[key]
|
||||
|
||||
|
||||
def _search_inventory(document, kind):
|
||||
"""Cache an immutable, address-sorted inventory for an unchanged document."""
|
||||
key = (id(document), kind)
|
||||
inventory = _search_inventory_cache.get(key)
|
||||
if inventory is None:
|
||||
if kind == "procedure":
|
||||
values = _procedure_map(document)
|
||||
elif kind == "string":
|
||||
values = _strings(document)
|
||||
elif kind == "name":
|
||||
values = {_hex(address): value for address, value in _name_map(document).items()}
|
||||
else:
|
||||
raise ValueError("Unknown inventory kind")
|
||||
values = _procedure_map(document) if kind == "procedure" else _strings(document)
|
||||
inventory = tuple(sorted(values.items(), key=lambda item: int(item[0], 16)))
|
||||
_search_inventory_cache[key] = inventory
|
||||
return inventory
|
||||
|
||||
|
||||
def _search_results(document, kind, params):
|
||||
def _validate_regex_node(node, inside_repeat=False):
|
||||
"""Reject regex structures with disproportionate or non-local evaluation cost."""
|
||||
forbidden = {
|
||||
sre_parse.ASSERT,
|
||||
sre_parse.ASSERT_NOT,
|
||||
sre_parse.GROUPREF,
|
||||
sre_parse.GROUPREF_EXISTS,
|
||||
}
|
||||
repeat_tokens = {sre_parse.MAX_REPEAT, sre_parse.MIN_REPEAT}
|
||||
possessive = getattr(sre_parse, "POSSESSIVE_REPEAT", None)
|
||||
if possessive is not None:
|
||||
repeat_tokens.add(possessive)
|
||||
for operation, argument in node:
|
||||
if operation in forbidden:
|
||||
raise ValueError("Regex lookarounds and backreferences are not supported")
|
||||
if operation in repeat_tokens:
|
||||
if inside_repeat:
|
||||
raise ValueError("Nested regex repetitions are not supported")
|
||||
minimum, maximum, child = argument
|
||||
if maximum == sre_parse.MAXREPEAT or maximum > 1000:
|
||||
raise ValueError("Unbounded or excessive regex repetitions are not supported")
|
||||
_validate_regex_node(child, True)
|
||||
elif operation == sre_parse.SUBPATTERN:
|
||||
_validate_regex_node(argument[-1], inside_repeat)
|
||||
elif operation == sre_parse.BRANCH:
|
||||
for branch in argument[1]:
|
||||
_validate_regex_node(branch, inside_repeat)
|
||||
|
||||
|
||||
def _search_page(document, kind, params):
|
||||
pattern = params.get("pattern")
|
||||
if not isinstance(pattern, str) or not pattern:
|
||||
raise InvalidRequestError("pattern must be a non-empty string")
|
||||
if not isinstance(pattern, str) or not pattern or len(pattern) > MAX_SEARCH_PATTERN_LENGTH:
|
||||
raise ValueError("pattern must contain between 1 and 256 characters")
|
||||
mode = params.get("mode", "literal")
|
||||
if mode not in ("literal", "regex"):
|
||||
raise InvalidRequestError("mode must be literal or regex")
|
||||
raise ValueError("mode must be literal or regex")
|
||||
case_sensitive = params.get("case_sensitive", False)
|
||||
if not isinstance(case_sensitive, bool):
|
||||
raise InvalidRequestError("case_sensitive must be a boolean")
|
||||
raise ValueError("case_sensitive must be a boolean")
|
||||
offset = params.get("offset", 0)
|
||||
limit = params.get("limit", 100)
|
||||
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
||||
raise ValueError("offset must be a non-negative integer")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 100:
|
||||
raise ValueError("limit must be an integer between 1 and 100")
|
||||
|
||||
if mode == "literal":
|
||||
needle = pattern if case_sensitive else pattern.casefold()
|
||||
matches = lambda value: needle in (value if case_sensitive else value.casefold())
|
||||
else:
|
||||
try:
|
||||
parsed = sre_parse.parse(pattern)
|
||||
_validate_regex_node(parsed)
|
||||
expression = re.compile(pattern, 0 if case_sensitive else re.IGNORECASE)
|
||||
except (re.error, OverflowError) as error:
|
||||
raise InvalidRequestError("Invalid regex pattern") from error
|
||||
except re.error as error:
|
||||
raise ValueError("Invalid regex pattern") from error
|
||||
matches = lambda value: expression.search(value) is not None
|
||||
|
||||
selected = []
|
||||
for item in _search_inventory(document, kind):
|
||||
if not matches(item[1]):
|
||||
continue
|
||||
selected.append(item)
|
||||
return [
|
||||
matching = [item for item in _search_inventory(document, kind) if matches(item[1])]
|
||||
selected = matching[offset:offset + limit]
|
||||
total = len(matching)
|
||||
next_offset = offset + len(selected)
|
||||
has_more = next_offset < total
|
||||
return {
|
||||
"items": [
|
||||
{
|
||||
"address": address,
|
||||
"value": value,
|
||||
"value": value[:MAX_SEARCH_VALUE_LENGTH],
|
||||
"value_truncated": len(value) > MAX_SEARCH_VALUE_LENGTH,
|
||||
}
|
||||
for address, value in selected
|
||||
]
|
||||
],
|
||||
"offset": offset,
|
||||
"limit": limit,
|
||||
"total": total,
|
||||
"next_offset": next_offset if has_more else None,
|
||||
"has_more": has_more,
|
||||
}
|
||||
|
||||
|
||||
def _page(values, offset, limit):
|
||||
"""Return one deterministically address-sorted page without crossing an unbounded map."""
|
||||
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
||||
raise ValueError("offset must be a non-negative integer")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
ordered = sorted(values.items(), key=lambda item: int(item[0], 16))
|
||||
selected = ordered[offset:offset + limit]
|
||||
total = len(ordered)
|
||||
next_offset = offset + len(selected)
|
||||
has_more = next_offset < total
|
||||
return {
|
||||
"items": [{"address": address, "value": value} for address, value in selected],
|
||||
"offset": offset,
|
||||
"limit": limit,
|
||||
"total": total,
|
||||
"next_offset": next_offset if has_more else None,
|
||||
"has_more": has_more,
|
||||
}
|
||||
|
||||
|
||||
def _unavailable(reason):
|
||||
@@ -422,6 +322,36 @@ def _unavailable(reason):
|
||||
return {"available": False, "reason": reason}
|
||||
|
||||
|
||||
def _offset(params, name):
|
||||
value = params.get(name, 0)
|
||||
if not isinstance(value, int) or isinstance(value, bool) or value < 0:
|
||||
raise ValueError("%s must be a non-negative integer" % name)
|
||||
return value
|
||||
|
||||
|
||||
def _collection_offset(params, name):
|
||||
values = params.get("collection_offset", {})
|
||||
if not isinstance(values, dict):
|
||||
raise ValueError("collection_offset must be an object")
|
||||
value = values.get(name, 0)
|
||||
if not isinstance(value, int) or isinstance(value, bool) or value < 0:
|
||||
raise ValueError("collection_offset.%s must be a non-negative integer" % name)
|
||||
return value
|
||||
|
||||
|
||||
def _bounded(items, offset, limit, total=None, scan_truncated=False):
|
||||
selected = items[offset:offset + limit]
|
||||
known_total = len(items) if total is None and not scan_truncated else total
|
||||
has_more = offset + len(selected) < len(items)
|
||||
return {
|
||||
"items": selected,
|
||||
"total": known_total,
|
||||
"returned": len(selected),
|
||||
"truncated": scan_truncated or has_more,
|
||||
"next_offset": offset + len(selected) if has_more else None,
|
||||
}
|
||||
|
||||
|
||||
def _name_map(document):
|
||||
result = {}
|
||||
for segment in document.getSegmentsList():
|
||||
@@ -432,24 +362,8 @@ def _name_map(document):
|
||||
return result
|
||||
|
||||
|
||||
def _render_instruction(segment, address):
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
return None
|
||||
arguments = [
|
||||
instruction.getFormattedArgument(index)
|
||||
for index in range(instruction.getArgumentCount())
|
||||
]
|
||||
suffix = ", ".join(value for value in arguments if value is not None)
|
||||
return "%s: %s%s" % (
|
||||
_hex(address),
|
||||
instruction.getInstructionString(),
|
||||
(" " + suffix) if suffix else "",
|
||||
)
|
||||
|
||||
|
||||
def _assembly(procedure):
|
||||
"""Render assembly while guarding against malformed instruction cycles."""
|
||||
def _assembly(procedure, limit=None):
|
||||
"""Render bounded assembly while guarding against malformed instruction cycles."""
|
||||
lines = []
|
||||
segment = procedure.getSegment()
|
||||
seen = set()
|
||||
@@ -461,9 +375,11 @@ def _assembly(procedure):
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
break
|
||||
line = _render_instruction(segment, address)
|
||||
if line is not None:
|
||||
lines.append(line)
|
||||
arguments = [instruction.getFormattedArgument(index) for index in range(instruction.getArgumentCount())]
|
||||
suffix = ", ".join(value for value in arguments if value is not None)
|
||||
lines.append("%s: %s%s" % (_hex(address), instruction.getInstructionString(), (" " + suffix) if suffix else ""))
|
||||
if limit is not None and len(lines) >= limit:
|
||||
return "\n".join(lines)
|
||||
length = instruction.getInstructionLength()
|
||||
if length <= 0:
|
||||
break
|
||||
@@ -471,30 +387,21 @@ def _assembly(procedure):
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _read_function_instructions(document, params):
|
||||
"""Read all raw instructions without invoking decompilation."""
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
addresses = _instruction_addresses(procedure)
|
||||
segment = procedure.getSegment()
|
||||
items = []
|
||||
for address in addresses:
|
||||
line = _render_instruction(segment, address)
|
||||
if line is not None:
|
||||
items.append(line)
|
||||
return {
|
||||
"procedure": _procedure_identity(procedure),
|
||||
"instructions": items,
|
||||
"limitations": [
|
||||
"Instruction text and ordering are Hopper-specific representations.",
|
||||
"The fast path does not decompile the procedure or scan whole-program names and strings.",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _analyze_function(document, params):
|
||||
"""Collect the complete function dossier for agent callers."""
|
||||
"""Collect a bounded, single-pass function dossier for agent callers."""
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
addresses = _instruction_addresses(procedure)
|
||||
limit = params.get("limit", 100)
|
||||
max_chars = params.get("max_pseudocode_chars", 20000)
|
||||
max_instructions = params.get("max_instructions", 500)
|
||||
pseudocode_offset = _offset(params, "pseudocode_offset")
|
||||
assembly_offset = _offset(params, "assembly_offset")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
if not isinstance(max_chars, int) or isinstance(max_chars, bool) or max_chars < 1 or max_chars > 100000:
|
||||
raise ValueError("max_pseudocode_chars must be between 1 and 100000")
|
||||
if not isinstance(max_instructions, int) or isinstance(max_instructions, bool) or max_instructions < 1 or max_instructions > 5000:
|
||||
raise ValueError("max_instructions must be between 1 and 5000")
|
||||
addresses, instruction_scan_truncated = _instruction_addresses(procedure, max_instructions)
|
||||
blocks = []
|
||||
all_blocks = list(procedure.basicBlockIterator())
|
||||
for block in all_blocks:
|
||||
@@ -508,8 +415,8 @@ def _analyze_function(document, params):
|
||||
"end": _hex(block.getEndingAddress()),
|
||||
"successors": sorted(set(successors), key=lambda value: int(value, 16)),
|
||||
})
|
||||
pseudo = _pseudocode(document, procedure) or ""
|
||||
assembly_lines = _assembly(procedure).splitlines()
|
||||
pseudo = procedure.decompile() or ""
|
||||
assembly_lines = _assembly(procedure).splitlines() if params.get("include_assembly", False) else []
|
||||
callers = sorted((_procedure_identity(item) for item in procedure.getAllCallerProcedures()), key=lambda item: int(item["address"], 16))
|
||||
callees = sorted((_procedure_identity(item) for item in procedure.getAllCalleeProcedures()), key=lambda item: int(item["address"], 16))
|
||||
comments = []
|
||||
@@ -543,14 +450,8 @@ def _analyze_function(document, params):
|
||||
incoming.append(item)
|
||||
if source in procedure_addresses:
|
||||
outgoing.append(item)
|
||||
string_map = {
|
||||
int(address, 16): value
|
||||
for address, value in _search_inventory(document, "string")
|
||||
}
|
||||
name_map = {
|
||||
int(address, 16): value
|
||||
for address, value in _search_inventory(document, "name")
|
||||
}
|
||||
string_map = {int(address, 16): value for address, value in _strings(document).items()}
|
||||
name_map = _name_map(document)
|
||||
referenced_strings = []
|
||||
referenced_names = []
|
||||
for edge in outgoing:
|
||||
@@ -562,27 +463,22 @@ def _analyze_function(document, params):
|
||||
comments.sort(key=lambda item: (int(item["address"], 16), item["kind"]))
|
||||
referenced_strings.sort(key=lambda item: (int(item["address"], 16), int(item["source_address"], 16)))
|
||||
referenced_names.sort(key=lambda item: (int(item["address"], 16), int(item["source_address"], 16)))
|
||||
pseudo_text = pseudo[pseudocode_offset:pseudocode_offset + max_chars]
|
||||
pseudo_next = pseudocode_offset + len(pseudo_text)
|
||||
def collection(name, items, scan_limited=False):
|
||||
return _bounded(items, _collection_offset(params, name), limit, None, scan_limited)
|
||||
return {
|
||||
"procedure": {
|
||||
**_procedure_identity(procedure),
|
||||
"signature": procedure.signatureString(),
|
||||
"locals": _procedure_locals(procedure),
|
||||
},
|
||||
"pseudocode": pseudo,
|
||||
"assembly": assembly_lines,
|
||||
"comments": comments,
|
||||
"callers": callers, "callees": callees,
|
||||
"incoming_references": incoming,
|
||||
"outgoing_references": outgoing,
|
||||
"referenced_strings": referenced_strings,
|
||||
"referenced_names": referenced_names,
|
||||
"basic_blocks": blocks,
|
||||
"limitations": [
|
||||
"Hopper's public Python API does not classify reference kinds.",
|
||||
"Hopper's public Python API does not expose equivalent external or thunk classification in this dossier.",
|
||||
"Unresolved indirect calls without reported target addresses are not represented as call edges.",
|
||||
"Pseudocode and assembly are provider-specific representations, not original source.",
|
||||
],
|
||||
"procedure": {"address": _hex(procedure.getEntryPoint()), "name": _procedure_name(procedure), "signature": procedure.signatureString(), "locals": _procedure_locals(procedure)},
|
||||
"pseudocode": {"text": pseudo_text, "total_chars": len(pseudo), "returned_chars": len(pseudo_text), "truncated": pseudo_next < len(pseudo), "next_offset": pseudo_next if pseudo_next < len(pseudo) else None},
|
||||
"assembly": _bounded(assembly_lines, assembly_offset, max_instructions),
|
||||
"comments": collection("comments", comments, instruction_scan_truncated),
|
||||
"callers": collection("callers", callers), "callees": collection("callees", callees),
|
||||
"incoming_references": collection("incoming_references", incoming, instruction_scan_truncated),
|
||||
"outgoing_references": collection("outgoing_references", outgoing, instruction_scan_truncated),
|
||||
"referenced_strings": collection("referenced_strings", referenced_strings, instruction_scan_truncated),
|
||||
"referenced_names": collection("referenced_names", referenced_names, instruction_scan_truncated),
|
||||
"basic_blocks": collection("basic_blocks", blocks),
|
||||
"instruction_scan": {"scanned": len(addresses), "truncated": instruction_scan_truncated},
|
||||
}
|
||||
|
||||
|
||||
@@ -591,36 +487,8 @@ def _dispatch(method, params):
|
||||
global _selected_document
|
||||
if method == "health":
|
||||
return {"name": "REA Hopper bridge", "version": "1.0.0", "run_id": REA_RUN_ID}
|
||||
if method in ("shutdown", "shutdown_document"):
|
||||
document = _session_document()
|
||||
if document is None:
|
||||
return {"shutdown": True, "analysis_stopped": True, "document_closed": True}
|
||||
if method == "shutdown" and not REA_OWNS_PROCESS_LIFETIME:
|
||||
return {
|
||||
"shutdown": True,
|
||||
"analysis_stopped": not document.backgroundProcessActive(),
|
||||
"document_closed": False,
|
||||
"document_retained": True,
|
||||
}
|
||||
if document.backgroundProcessActive():
|
||||
document.requestBackgroundProcessStop()
|
||||
if method == "shutdown" and REA_OWNS_PROCESS_LIFETIME:
|
||||
return {
|
||||
"shutdown": True,
|
||||
"analysis_stopped": not document.backgroundProcessActive(),
|
||||
"document_closed": False,
|
||||
"cleanup_required": True,
|
||||
}
|
||||
if document.backgroundProcessActive():
|
||||
document.waitForBackgroundProcessToEnd()
|
||||
document.closeDocument()
|
||||
analysis_stopped = not document.backgroundProcessActive()
|
||||
document_closed = _session_document() is None
|
||||
return {
|
||||
"shutdown": True,
|
||||
"analysis_stopped": analysis_stopped,
|
||||
"document_closed": document_closed,
|
||||
}
|
||||
if method == "shutdown":
|
||||
return {"shutdown": True}
|
||||
if method == "list_documents":
|
||||
return [document.getDocumentName() for document in Document.getAllDocuments()]
|
||||
if method == "current_document":
|
||||
@@ -631,54 +499,9 @@ def _dispatch(method, params):
|
||||
return _selected_document
|
||||
|
||||
document = _document(params.get("document"))
|
||||
if method in EXHAUSTIVE_ANALYSIS_METHODS:
|
||||
_api().require_analysis_complete(document, method)
|
||||
|
||||
if method == "analyze_function":
|
||||
return _analyze_function(document, params)
|
||||
if method == "read_function_instructions":
|
||||
return _read_function_instructions(document, params)
|
||||
if method == "read_bytes":
|
||||
length = params.get("length", 256)
|
||||
if isinstance(length, bool) or not isinstance(length, int):
|
||||
raise InvalidRequestError("Byte-read length must be an integer")
|
||||
if length < 1:
|
||||
raise InvalidRequestError("Byte-read length must be at least 1")
|
||||
reader = getattr(document, "readBytes", None)
|
||||
if not callable(reader):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose readBytes"
|
||||
)
|
||||
address = _address(document, params.get("address"))
|
||||
value = reader(address, length)
|
||||
if not isinstance(value, (bytes, bytearray)):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API returned an unsupported byte representation"
|
||||
)
|
||||
data = bytes(value)
|
||||
return {
|
||||
"address": _hex(address),
|
||||
"requested_bytes": length,
|
||||
"returned_bytes": len(data),
|
||||
"bytes_hex": data.hex(),
|
||||
"complete": len(data) == length,
|
||||
}
|
||||
if method == "address_to_file_offset":
|
||||
mapper = getattr(document, "getFileOffsetFromAddress", None)
|
||||
if not callable(mapper):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose getFileOffsetFromAddress"
|
||||
)
|
||||
address = _address(document, params.get("address"))
|
||||
offset = mapper(address)
|
||||
if (
|
||||
isinstance(offset, bool)
|
||||
or not isinstance(offset, int)
|
||||
or offset in BAD_ADDRESSES
|
||||
or offset < 0
|
||||
):
|
||||
raise InvalidRequestError("Address has no authoritative file-offset mapping")
|
||||
return {"address": _hex(address), "file_offset": offset}
|
||||
if method == "resolve_containing_procedure":
|
||||
address = _address(document, params.get("address"))
|
||||
procedure, reason = _containing_procedure(document, address)
|
||||
@@ -713,7 +536,7 @@ def _dispatch(method, params):
|
||||
else:
|
||||
result = document.getInstructionStart(max(0, target - 1))
|
||||
if result in BAD_ADDRESSES:
|
||||
raise InvalidRequestError("No adjacent address")
|
||||
raise ValueError("No adjacent address")
|
||||
return _hex(result)
|
||||
if method == "list_segments":
|
||||
result = []
|
||||
@@ -745,24 +568,27 @@ def _dispatch(method, params):
|
||||
})
|
||||
return result
|
||||
if method == "list_procedures":
|
||||
return [{"address": address, "value": value} for address, value in _search_inventory(document, "procedure")]
|
||||
return _page(_procedure_map(document), params.get("offset", 0), params.get("limit", 100))
|
||||
if method == "list_strings":
|
||||
values = dict(_search_inventory(document, "string"))
|
||||
values = _strings(document)
|
||||
requested = params.get("address")
|
||||
if requested is not None:
|
||||
key = _hex(_address(document, requested))
|
||||
values = {key: values[key]} if key in values else {}
|
||||
return [{"address": address, "value": value} for address, value in values.items()]
|
||||
return _page(values, params.get("offset", 0), params.get("limit", 100))
|
||||
if method == "list_names":
|
||||
result = dict(_search_inventory(document, "name"))
|
||||
result = {}
|
||||
for segment in document.getSegmentsList():
|
||||
for item in segment.getNamedAddresses():
|
||||
result[_hex(item)] = segment.getNameAtAddress(item)
|
||||
requested = params.get("address")
|
||||
if requested is not None:
|
||||
key = _hex(_address(document, requested))
|
||||
result = {key: result[key]} if key in result else {}
|
||||
return [{"address": address, "value": value} for address, value in result.items()]
|
||||
return _page(result, params.get("offset", 0), params.get("limit", 100))
|
||||
if method in ("search_procedures", "search_strings"):
|
||||
kind = "procedure" if method == "search_procedures" else "string"
|
||||
return _search_results(document, kind, params)
|
||||
return _search_page(document, kind, params)
|
||||
if method.startswith("procedure_"):
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
if method == "procedure_address":
|
||||
@@ -770,38 +596,23 @@ def _dispatch(method, params):
|
||||
if method == "procedure_assembly":
|
||||
return _assembly(procedure)
|
||||
if method == "procedure_pseudo_code":
|
||||
return _pseudocode(document, procedure)
|
||||
return procedure.decompile()
|
||||
if method == "procedure_callers":
|
||||
return sorted((_hex(item.getEntryPoint()) for item in procedure.getAllCallerProcedures()), key=lambda value: int(value, 16))
|
||||
return [_procedure_name(item) for item in procedure.getAllCallerProcedures()]
|
||||
if method == "procedure_callees":
|
||||
return sorted((_hex(item.getEntryPoint()) for item in procedure.getAllCalleeProcedures()), key=lambda value: int(value, 16))
|
||||
return [_procedure_name(item) for item in procedure.getAllCalleeProcedures()]
|
||||
if method == "procedure_info":
|
||||
blocks = list(procedure.basicBlockIterator())
|
||||
length = sum(max(0, block.getEndingAddress() - block.getStartingAddress()) for block in blocks)
|
||||
return {
|
||||
"name": _procedure_name(procedure),
|
||||
"entrypoint": _hex(procedure.getEntryPoint()),
|
||||
"basicblock_count": procedure.getBasicBlockCount(),
|
||||
"length": length,
|
||||
"signature": procedure.signatureString(),
|
||||
"locals": _procedure_locals(procedure),
|
||||
"classification": None,
|
||||
"body": _procedure_identity(procedure)["body"],
|
||||
}
|
||||
return {"name": _procedure_name(procedure), "entrypoint": _hex(procedure.getEntryPoint()), "basicblock_count": procedure.getBasicBlockCount(), "length": length, "signature": procedure.signatureString(), "locals": _procedure_locals(procedure)}
|
||||
if method == "set_address_name":
|
||||
address = _address(document, params.get("address"))
|
||||
try:
|
||||
result = document.setNameAtAddress(address, params["name"])
|
||||
finally:
|
||||
_invalidate_search_inventory(document)
|
||||
_invalidate_pseudocode(document)
|
||||
result = document.setNameAtAddress(address, params["name"])
|
||||
_invalidate_search_inventory(document)
|
||||
return result
|
||||
if method == "set_addresses_names":
|
||||
try:
|
||||
result = {key: document.setNameAtAddress(_address(document, key), value) for key, value in params["names"].items()}
|
||||
finally:
|
||||
_invalidate_search_inventory(document)
|
||||
_invalidate_pseudocode(document)
|
||||
result = {key: document.setNameAtAddress(_address(document, key), value) for key, value in params["names"].items()}
|
||||
_invalidate_search_inventory(document)
|
||||
return result
|
||||
if method in ("set_comment", "set_inline_comment"):
|
||||
address = _address(document, params.get("address"))
|
||||
@@ -809,7 +620,6 @@ def _dispatch(method, params):
|
||||
setter = segment.setCommentAtAddress if method == "set_comment" else segment.setInlineCommentAtAddress
|
||||
getter = segment.getCommentAtAddress if method == "set_comment" else segment.getInlineCommentAtAddress
|
||||
setter(address, params["comment"])
|
||||
_invalidate_pseudocode(document)
|
||||
return getter(address) == params["comment"]
|
||||
if method == "list_bookmarks":
|
||||
return [{"address": _hex(item), "name": document.getBookmarkName(item)} for item in document.getBookmarks()]
|
||||
@@ -821,111 +631,38 @@ def _dispatch(method, params):
|
||||
address = _address(document, params.get("address"))
|
||||
document.removeBookmarkAtAddress(address)
|
||||
return not document.hasBookmarkAtAddress(address)
|
||||
raise InvalidRequestError("Unknown bridge method")
|
||||
raise ValueError("Unknown bridge method")
|
||||
|
||||
|
||||
def _serve_connection(connection):
|
||||
"""Serve one capability-authenticated NDJSON connection."""
|
||||
"""Serve one size-bounded, capability-authenticated NDJSON connection."""
|
||||
file = connection.makefile("rwb")
|
||||
while True:
|
||||
line = file.readline()
|
||||
if not line:
|
||||
line = file.readline(MAX_LINE_BYTES + 1)
|
||||
if not line or len(line) > MAX_LINE_BYTES:
|
||||
break
|
||||
request_id = None
|
||||
authenticated = False
|
||||
should_stop = False
|
||||
try:
|
||||
try:
|
||||
request = json.loads(line.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise InvalidRequestError("Invalid bridge request JSON") from error
|
||||
if not isinstance(request, dict) or set(request) != {"id", "token", "method", "params"}:
|
||||
raise InvalidRequestError("Invalid bridge request shape")
|
||||
request = json.loads(line.decode("utf-8"))
|
||||
if set(request) != {"id", "token", "method", "params"}:
|
||||
raise ValueError("Invalid bridge request shape")
|
||||
request_id = request["id"]
|
||||
if isinstance(request_id, bool) or not isinstance(request_id, int) or request_id < 0:
|
||||
raise InvalidRequestError("Invalid bridge request id")
|
||||
if (
|
||||
not isinstance(request["method"], str) or not request["method"]
|
||||
or not isinstance(request["params"], dict)
|
||||
):
|
||||
raise InvalidRequestError("Invalid bridge method or parameters")
|
||||
if (
|
||||
not isinstance(request["token"], str) or not request["token"].isascii()
|
||||
or not hmac.compare_digest(request["token"], REA_TOKEN)
|
||||
):
|
||||
if not isinstance(request["token"], str) or not hmac.compare_digest(request["token"], REA_TOKEN):
|
||||
raise PermissionError("Invalid bridge capability")
|
||||
authenticated = True
|
||||
_write_message(file, {"id": request_id, "event": {
|
||||
"type": "progress",
|
||||
"phase": "hopper_bridge",
|
||||
"completed": 0,
|
||||
"total": 1,
|
||||
"message": "Hopper bridge started request",
|
||||
}})
|
||||
result = _dispatch(request["method"], request["params"])
|
||||
should_stop = request["method"] == "shutdown_document" or (
|
||||
request["method"] == "shutdown" and not result.get("cleanup_required", False)
|
||||
)
|
||||
safe_result = _json_safe(result)
|
||||
_write_message(file, {"id": request_id, "event": {
|
||||
"type": "progress",
|
||||
"phase": "hopper_bridge",
|
||||
"completed": 1,
|
||||
"total": 1,
|
||||
"message": "Hopper bridge completed request",
|
||||
"terminal": True,
|
||||
}})
|
||||
response = {"id": request_id, "result": safe_result}
|
||||
should_stop = request["method"] == "shutdown"
|
||||
response = {"id": request_id, "result": _json_safe(result)}
|
||||
except Exception as error:
|
||||
diagnostic = _safe_diagnostic(error)
|
||||
if authenticated:
|
||||
_write_message(file, {"id": request_id, "event": {
|
||||
"type": "diagnostic",
|
||||
"error": diagnostic,
|
||||
}})
|
||||
response_id = request_id if (
|
||||
isinstance(request_id, int)
|
||||
and not isinstance(request_id, bool)
|
||||
and request_id >= 0
|
||||
) else 0
|
||||
response = {"id": response_id, "error": diagnostic}
|
||||
_write_message(file, response)
|
||||
response = {"id": request_id if isinstance(request_id, int) else 0, "error": {"code": -32000, "message": str(error)[:512]}}
|
||||
file.write((json.dumps(response, separators=(",", ":")) + "\n").encode("utf-8"))
|
||||
file.flush()
|
||||
if should_stop:
|
||||
break
|
||||
file.close()
|
||||
connection.close()
|
||||
|
||||
|
||||
def _write_message(file, message):
|
||||
"""Write and flush one compact bridge message."""
|
||||
file.write((json.dumps(message, separators=(",", ":")) + "\n").encode("utf-8"))
|
||||
file.flush()
|
||||
|
||||
|
||||
def _diagnostic_type(error):
|
||||
if isinstance(error, CapabilityUnavailableError):
|
||||
return "capability_unavailable"
|
||||
if isinstance(error, PermissionError):
|
||||
return "authorization"
|
||||
if isinstance(error, InvalidRequestError):
|
||||
return "invalid_request"
|
||||
return "bridge_exception"
|
||||
|
||||
|
||||
def _safe_diagnostic(error):
|
||||
"""Project one exception without retaining provider or credential text."""
|
||||
diagnostic_type = _diagnostic_type(error)
|
||||
if diagnostic_type == "capability_unavailable":
|
||||
message = str(error)
|
||||
elif diagnostic_type == "authorization":
|
||||
message = "Invalid bridge capability"
|
||||
elif diagnostic_type == "invalid_request":
|
||||
message = "Invalid Hopper bridge request"
|
||||
else:
|
||||
message = "%s: Hopper bridge operation failed" % type(error).__name__
|
||||
return {"code": -32000, "message": message, "type": diagnostic_type}
|
||||
|
||||
|
||||
def _run():
|
||||
"""Own a permission-restricted, single-client Unix socket for this bridge."""
|
||||
if os.path.exists(REA_SOCKET):
|
||||
@@ -944,7 +681,5 @@ def _run():
|
||||
|
||||
|
||||
# Hopper's public objects are bound to its dedicated Python execution thread.
|
||||
# Keep dispatch on that thread; moving calls to a worker can deadlock.
|
||||
if __name__ == "__main__":
|
||||
_bind_session_document()
|
||||
_run()
|
||||
# Keep dispatch on that thread; moving calls to an arbitrary worker can deadlock.
|
||||
_run()
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
"""REA offline adapter using mitmproxy's unchanged native tnetstring decoder.
|
||||
|
||||
No FlowReader migrations, HAR conversion, proxy listener, or target requests.
|
||||
The containing mitmdump process and its configuration directory are REA-owned.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import resource
|
||||
from pathlib import Path
|
||||
|
||||
from mitmproxy import ctx, version
|
||||
from mitmproxy.io import tnetstring
|
||||
|
||||
PROFILE = "12.2.3"
|
||||
OUTPUT_BYTES = 96 * 1024 * 1024
|
||||
CREDENTIALS = {b"authorization", b"proxy-authorization", b"cookie", b"set-cookie"}
|
||||
|
||||
|
||||
class CaptureFailure(Exception):
|
||||
def __init__(self, reason, message, pointer=""):
|
||||
self.reason, self.pointer = reason, pointer
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
def pointer(parent, key):
|
||||
return parent + "/" + str(key).replace("~", "~0").replace("/", "~1")
|
||||
|
||||
|
||||
class BoundedReader:
|
||||
"""Refuse malicious declared lengths before the upstream parser allocates."""
|
||||
def __init__(self, handle, size):
|
||||
self.handle, self.size = handle, size
|
||||
|
||||
def read(self, count):
|
||||
if count < 0 or count > self.size - self.handle.tell():
|
||||
raise CaptureFailure("format", "Native record declares bytes beyond the retained capture.")
|
||||
return self.handle.read(count)
|
||||
|
||||
|
||||
def project_record(state, secrets):
|
||||
binaries, numbers, redactions = [], [], []
|
||||
byte_secrets = []
|
||||
for secret in secrets:
|
||||
try:
|
||||
byte_secrets.append(secret.encode("utf-8", errors="strict"))
|
||||
except UnicodeEncodeError:
|
||||
# Non-scalar JSON strings have no UTF-8 byte representation.
|
||||
# Keep them in secrets for the ordinary string projection.
|
||||
pass
|
||||
|
||||
def redact(text, path, original):
|
||||
if any(secret in text or secret in original for secret in secrets):
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
|
||||
return None
|
||||
return text
|
||||
|
||||
def transport_value(value, path, transport_url):
|
||||
if not (transport_url or re.fullmatch(r"/(?:backup/)*request/(?:path|authority)", path)):
|
||||
return value, False
|
||||
binary = isinstance(value, bytes)
|
||||
pattern = rb"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@" if binary else r"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@"
|
||||
replacement = rb"\1" if binary else r"\1"
|
||||
separator = b"@" if binary else "@"
|
||||
safe_url = re.sub(pattern, replacement, value, flags=re.I)
|
||||
if path.endswith("/authority") and separator in safe_url:
|
||||
safe_url = safe_url.rsplit(separator, 1)[1]
|
||||
changed = safe_url != value
|
||||
if changed:
|
||||
redactions.append({"pointer": path, "reason": "transport-credential"})
|
||||
return safe_url, changed
|
||||
|
||||
def validate_omitted(value, path, depth):
|
||||
before = len(binaries), len(numbers), len(redactions)
|
||||
visit(value, path, depth)
|
||||
del binaries[before[0]:]
|
||||
del numbers[before[1]:]
|
||||
del redactions[before[2]:]
|
||||
|
||||
def visit(value, path, depth=0, credential=False, transport_url=False):
|
||||
if depth > 64:
|
||||
raise CaptureFailure("input-limit", "Native capture exceeds the 64-level evidence nesting budget.", path)
|
||||
if credential:
|
||||
validate_omitted(value, path, depth)
|
||||
redactions.append({"pointer": path, "reason": "transport-credential"})
|
||||
if isinstance(value, bytes):
|
||||
binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted", "content_base64": None, "bytes": None, "sha256": None})
|
||||
return None
|
||||
if isinstance(value, bytes):
|
||||
safe_bytes, credential_url = transport_value(value, path, transport_url)
|
||||
content_base64 = base64.b64encode(value).decode("ascii")
|
||||
digest = hashlib.sha256(value).hexdigest()
|
||||
hidden = any(secret in value or secret in safe_bytes for secret in byte_secrets) or any(secret in content_base64 or secret in digest for secret in secrets)
|
||||
safe_text = None
|
||||
try:
|
||||
safe_text = safe_bytes.decode("utf-8", errors="strict")
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
if hidden:
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
|
||||
exclude = hidden or credential_url
|
||||
binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted" if exclude else "retained", "content_base64": None if exclude else content_base64, "bytes": None if exclude else len(value), "sha256": None if exclude else digest})
|
||||
if hidden:
|
||||
return None
|
||||
return safe_text
|
||||
if isinstance(value, str):
|
||||
safe_text, _ = transport_value(value, path, transport_url)
|
||||
return redact(safe_text, path, value)
|
||||
if isinstance(value, bool) or value is None:
|
||||
return value
|
||||
if isinstance(value, (int, float)):
|
||||
literal = repr(value)
|
||||
if any(secret in literal for secret in secrets):
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value"})
|
||||
return None
|
||||
numbers.append({"pointer": path, "producer_type": "integer" if isinstance(value, int) else "float", "literal": literal})
|
||||
return value if (abs(value) <= 9007199254740991 if isinstance(value, int) else math.isfinite(value)) else None
|
||||
if isinstance(value, (list, tuple)):
|
||||
fields = re.fullmatch(r"/(?:backup/)*(?:request|response)/(?:headers|trailers)", path)
|
||||
if fields:
|
||||
result = []
|
||||
for index, field in enumerate(value):
|
||||
if not isinstance(field, (list, tuple)) or len(field) != 2 or not isinstance(field[0], bytes):
|
||||
raise CaptureFailure("format", "Native header/trailer is not an ordered byte pair.", pointer(path, index))
|
||||
result.append([visit(field[0], pointer(pointer(path, index), 0), depth + 2), visit(field[1], pointer(pointer(path, index), 1), depth + 2, field[0].lower() in CREDENTIALS, field[0].lower() in {b"location", b"referer", b"origin"})])
|
||||
return result
|
||||
return [visit(item, pointer(path, index), depth + 1) for index, item in enumerate(value)]
|
||||
if isinstance(value, dict):
|
||||
result = {}
|
||||
for key, item in value.items():
|
||||
if not isinstance(key, str):
|
||||
raise CaptureFailure("format", "Native record has a non-string dictionary key.", path)
|
||||
if any(secret in key for secret in secrets):
|
||||
# Validate the omitted subtree too, then discard all of its
|
||||
# coordinates rather than inventing a replacement identity.
|
||||
validate_omitted(item, pointer(path, key), depth + 1)
|
||||
redactions.append({"pointer": path, "reason": "explicit-sensitive-value", "scope": "property-name"})
|
||||
continue
|
||||
result[key] = visit(item, pointer(path, key), depth + 1)
|
||||
return result
|
||||
raise CaptureFailure("decoder", "Native parser returned an unexpected value type: " + type(value).__name__ + ".", path)
|
||||
|
||||
return {"reported": visit(state, ""), "binary_fields": binaries, "numeric_literals": numbers, "redactions": redactions}
|
||||
|
||||
|
||||
def decode(request):
|
||||
if version.VERSION != PROFILE:
|
||||
raise CaptureFailure("unsupported", "Expected mitmproxy " + PROFILE + "; observed " + version.VERSION + ".")
|
||||
records = []
|
||||
capture = Path(request["snapshot_path"])
|
||||
size = capture.stat().st_size
|
||||
with capture.open("rb") as handle:
|
||||
reader = BoundedReader(handle, size)
|
||||
while handle.tell() < size:
|
||||
start = handle.tell()
|
||||
try:
|
||||
state = tnetstring.load(reader)
|
||||
except CaptureFailure:
|
||||
raise
|
||||
except RecursionError:
|
||||
raise CaptureFailure("input-limit", "Native parser exceeded its recursion budget at byte offset " + str(start) + ".") from None
|
||||
except (ValueError, IndexError, UnicodeError, EOFError):
|
||||
raise CaptureFailure("format", "Malformed native record at byte offset " + str(start) + ".") from None
|
||||
if not isinstance(state, dict):
|
||||
raise CaptureFailure("format", "Native flow record must be a dictionary at byte offset " + str(start) + ".")
|
||||
records.append({"ordinal": len(records), "location": {"kind": "byte-range", "offset": start, "bytes": handle.tell() - start}, **project_record(state, request["sensitive_values"]), "limitations": ["Fields are original native states without FlowReader migration. Binary fields contain exact producer bytes; reported UTF-8 strings for those fields are derived display views. Unknown flow/version extensions are retained without interpretation."]})
|
||||
return {"decoder": {"id": "mitmproxy-native-tnetstring", "name": "REA offline native mitmproxy adapter", "version": PROFILE}, "container": {"reported": None, "numeric_literals": [], "redactions": [], "records_pointer": None}, "total_records": len(records), "records": records}
|
||||
|
||||
|
||||
def failure_reply(error):
|
||||
"""Keep producer constraints, resource exhaustion and adapter defects distinct."""
|
||||
if isinstance(error, CaptureFailure):
|
||||
return {"ok": False, "reason": error.reason, "message": str(error), "pointer": error.pointer}
|
||||
if isinstance(error, MemoryError):
|
||||
return {"ok": False, "reason": "resource-limit", "message": "Native decoder exhausted its 768 MiB address-space budget.", "pointer": ""}
|
||||
return {"ok": False, "reason": "decoder", "message": "Offline native decoder failed unexpectedly: " + type(error).__name__ + ".", "pointer": ""}
|
||||
|
||||
|
||||
class OfflineCapture:
|
||||
def load(self, loader):
|
||||
loader.add_option("rea_request_path", str, "", "REA private offline capture request")
|
||||
|
||||
def running(self):
|
||||
resource.setrlimit(resource.RLIMIT_AS, (768 * 1024 * 1024, 768 * 1024 * 1024))
|
||||
resource.setrlimit(resource.RLIMIT_CPU, (30, 30))
|
||||
resource.setrlimit(resource.RLIMIT_FSIZE, (OUTPUT_BYTES, OUTPUT_BYTES))
|
||||
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
|
||||
request = json.loads(Path(ctx.options.rea_request_path).read_text(encoding="utf-8"))
|
||||
try:
|
||||
response = {"ok": True, "value": decode(request)}
|
||||
except Exception as error:
|
||||
response = failure_reply(error)
|
||||
try:
|
||||
try:
|
||||
output = json.dumps(response, ensure_ascii=True, allow_nan=False, separators=(",", ":")).encode("ascii")
|
||||
except Exception as error:
|
||||
output = json.dumps(failure_reply(error), separators=(",", ":")).encode("ascii")
|
||||
if len(output) > OUTPUT_BYTES:
|
||||
output = b'{"ok":false,"reason":"limit","message":"Native reply exceeds the complete-evidence output budget.","pointer":""}'
|
||||
Path(request["reply_path"]).write_bytes(output)
|
||||
finally:
|
||||
ctx.master.shutdown()
|
||||
|
||||
|
||||
addons = [OfflineCapture()]
|
||||
@@ -1,27 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
struct ChildBatch<Element> {
|
||||
let values: [Element]
|
||||
let complete: Bool
|
||||
}
|
||||
|
||||
struct ChildCount {
|
||||
let value: Int?
|
||||
let error: Int32?
|
||||
}
|
||||
|
||||
func serializeHelperOutput(_ value: Any) throws -> Data {
|
||||
try JSONSerialization.data(withJSONObject: value, options: [.sortedKeys, .withoutEscapingSlashes])
|
||||
}
|
||||
|
||||
func captureChildCount(status: Int32, success: Int32, value: Int) -> ChildCount {
|
||||
guard status == success else { return ChildCount(value: nil, error: status) }
|
||||
return ChildCount(value: value, error: nil)
|
||||
}
|
||||
|
||||
func captureChildValues<Element>(requestedCount: Int, copy: () -> [Element]?) -> ChildBatch<Element> {
|
||||
guard requestedCount > 0 else { return ChildBatch(values: [], complete: true) }
|
||||
let returned = copy() ?? []
|
||||
let values = Array(returned.prefix(requestedCount))
|
||||
return ChildBatch(values: values, complete: values.count == requestedCount)
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
import AppKit
|
||||
import ApplicationServices
|
||||
import CryptoKit
|
||||
import ScreenCaptureKit
|
||||
import Darwin
|
||||
|
||||
struct Request: Decodable {
|
||||
var pid: Int32
|
||||
var window_id: UInt32
|
||||
var executable: String
|
||||
var sha256: String
|
||||
var launch_time: Double?
|
||||
var screenshot: Bool
|
||||
var accessibility: Bool
|
||||
var max_nodes: Int
|
||||
var action: Action?
|
||||
}
|
||||
struct Action: Decodable { var kind: String; var path: [Int]?; var direction: String?; var text: String? }
|
||||
struct BoundaryFailure: Error { var code: String; var message: String }
|
||||
func fail(_ code: String, _ message: String) throws -> Never { throw BoundaryFailure(code: code, message: message) }
|
||||
func attribute(_ element: AXUIElement, _ key: String) -> CFTypeRef? {
|
||||
var value: CFTypeRef?
|
||||
return AXUIElementCopyAttributeValue(element, key as CFString, &value) == .success ? value : nil
|
||||
}
|
||||
func childCount(_ element: AXUIElement) -> ChildCount {
|
||||
var count = 0
|
||||
let status = AXUIElementGetAttributeValueCount(element, kAXChildrenAttribute as CFString, &count)
|
||||
return captureChildCount(status: status.rawValue, success: AXError.success.rawValue, value: count)
|
||||
}
|
||||
func children(_ element: AXUIElement, count: Int) -> ChildBatch<AXUIElement> {
|
||||
captureChildValues(requestedCount: count) {
|
||||
var value: CFArray?
|
||||
guard AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, 0, count, &value) == .success else { return nil }
|
||||
return value as? [AXUIElement]
|
||||
}
|
||||
}
|
||||
func text(_ element: AXUIElement, _ key: String) -> Any { (attribute(element, key) as? String) ?? NSNull() as Any }
|
||||
func windowBounds(_ element: AXUIElement) -> CGRect? {
|
||||
guard let p = attribute(element, kAXPositionAttribute), let s = attribute(element, kAXSizeAttribute),
|
||||
CFGetTypeID(p) == AXValueGetTypeID(), CFGetTypeID(s) == AXValueGetTypeID() else { return nil }
|
||||
var point = CGPoint.zero; var size = CGSize.zero
|
||||
guard AXValueGetValue(p as! AXValue, .cgPoint, &point), AXValueGetValue(s as! AXValue, .cgSize, &size) else { return nil }
|
||||
return CGRect(origin: point, size: size)
|
||||
}
|
||||
func observe(_ request: Request) async throws -> [String: Any] {
|
||||
guard let app = NSRunningApplication(processIdentifier: request.pid), let url = app.executableURL else {
|
||||
try fail("target-mismatch", "Selected PID does not expose an application executable URL")
|
||||
}
|
||||
guard let canonicalPointer = realpath(url.path, nil) else { try fail("target-mismatch", "Running executable path cannot be resolved") }
|
||||
defer { free(canonicalPointer) }
|
||||
let canonicalExecutable = String(cString: canonicalPointer)
|
||||
guard canonicalExecutable == request.executable else {
|
||||
try fail("target-mismatch", "Selected PID executable \(canonicalExecutable) differs from active target \(request.executable)")
|
||||
}
|
||||
guard let launch = app.launchDate?.timeIntervalSince1970 else {
|
||||
try fail("process-identity-unavailable", "Selected application does not expose a launch time; stable PID reuse checks are unavailable")
|
||||
}
|
||||
if let expected = request.launch_time, expected != launch { try fail("process-replaced", "Selected process was replaced") }
|
||||
let executableSize = (try FileManager.default.attributesOfItem(atPath: url.path)[.size] as? NSNumber)?.int64Value ?? -1
|
||||
guard executableSize >= 0 && executableSize <= 256 * 1024 * 1024 else { try fail("target-limit", "Running executable exceeds the 256 MiB identity-check budget") }
|
||||
let executableBytes = try Data(contentsOf: url, options: .mappedIfSafe)
|
||||
guard SHA256.hash(data: executableBytes).map({ String(format: "%02x", $0) }).joined() == request.sha256 else {
|
||||
try fail("target-mismatch", "Running executable bytes differ from the active target digest")
|
||||
}
|
||||
guard let windows = CGWindowListCopyWindowInfo([.optionIncludingWindow], request.window_id) as? [[String: Any]],
|
||||
windows.count == 1, let window = windows.first,
|
||||
(window[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == request.pid,
|
||||
(window[kCGWindowLayer as String] as? NSNumber)?.intValue == 0,
|
||||
let boundsDictionary = window[kCGWindowBounds as String] as? [String: Any],
|
||||
let bounds = CGRect(dictionaryRepresentation: boundsDictionary as CFDictionary) else {
|
||||
try fail("window-mismatch", "Selected window is absent or belongs to another process")
|
||||
}
|
||||
if request.screenshot && !CGPreflightScreenCaptureAccess() {
|
||||
try fail("screen-recording-denied", "Grant Screen Recording to the REA host in System Settings > Privacy & Security, then retry; no broad capture fallback is used")
|
||||
}
|
||||
var selected: AXUIElement?
|
||||
if request.accessibility || request.action != nil {
|
||||
guard AXIsProcessTrusted() else { try fail("accessibility-denied", "Grant Accessibility to the REA host in System Settings > Privacy & Security, then retry") }
|
||||
let application = AXUIElementCreateApplication(request.pid)
|
||||
AXUIElementSetMessagingTimeout(application, 2)
|
||||
guard let axWindows = attribute(application, kAXWindowsAttribute) as? [AXUIElement] else {
|
||||
try fail("accessibility-unavailable", "Selected application does not expose accessibility windows")
|
||||
}
|
||||
let matches = axWindows.filter { windowBounds($0) == bounds }
|
||||
guard matches.count == 1 else { try fail("ambiguous-window", "Accessibility window geometry does not identify exactly one selected window; selected CG bounds \(bounds), AX candidates \(axWindows.map { String(describing: windowBounds($0)) })") }
|
||||
selected = matches[0]
|
||||
}
|
||||
if let action = request.action, let root = selected {
|
||||
var element = root
|
||||
for index in action.path ?? [] {
|
||||
var selectedChild: CFArray?
|
||||
let count = childCount(element)
|
||||
guard let available = count.value else {
|
||||
try fail("element-count-unavailable", "Cannot validate selected accessibility path because child count failed with AXError \(count.error ?? -1)")
|
||||
}
|
||||
guard index >= 0 && index < available,
|
||||
AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, index, 1, &selectedChild) == .success,
|
||||
let item = (selectedChild as? [AXUIElement])?.first else { try fail("element-missing", "Selected accessibility path no longer exists") }
|
||||
element = item
|
||||
}
|
||||
var owner: pid_t = 0
|
||||
guard AXUIElementGetPid(element, &owner) == .success && owner == request.pid else { try fail("element-owner-mismatch", "Selected element belongs to another process") }
|
||||
let outcome: AXError
|
||||
switch action.kind {
|
||||
case "click": outcome = AXUIElementPerformAction(element, kAXPressAction as CFString)
|
||||
case "scroll": outcome = AXUIElementPerformAction(element, (action.direction == "increment" ? kAXIncrementAction : kAXDecrementAction) as CFString)
|
||||
case "key-entry": outcome = AXUIElementSetAttributeValue(element, kAXValueAttribute as CFString, (action.text ?? "") as CFString)
|
||||
default: try fail("unsupported-action", "Only selected-element press, increment/decrement and text-value entry are admitted")
|
||||
}
|
||||
guard outcome == .success else { try fail("action-failed", "Accessibility action failed with AXError \(outcome.rawValue)") }
|
||||
}
|
||||
var nodes: [[String: Any]] = []; var truncated = false; var gaps: [String] = []
|
||||
if request.accessibility, let root = selected {
|
||||
var pending: [(AXUIElement, [Int])] = [(root, [])]
|
||||
while let (element, path) = pending.popLast() {
|
||||
if nodes.count >= request.max_nodes { truncated = true; break }
|
||||
let childCountResult = childCount(element)
|
||||
var actions: CFArray?
|
||||
AXUIElementCopyActionNames(element, &actions)
|
||||
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": text(element, kAXValueAttribute), "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
|
||||
guard let totalChildren = childCountResult.value else {
|
||||
truncated = true
|
||||
gaps.append("AX child count unavailable at path \(path): AXError \(childCountResult.error ?? -1)")
|
||||
continue
|
||||
}
|
||||
if path.count >= 32 { if totalChildren > 0 { truncated = true }; continue }
|
||||
let available = max(0, request.max_nodes - nodes.count - pending.count)
|
||||
let count = min(available, totalChildren)
|
||||
let batch = children(element, count: count)
|
||||
if !batch.complete { truncated = true }
|
||||
if totalChildren > count { truncated = true }
|
||||
for (index, item) in batch.values.enumerated().reversed() {
|
||||
pending.append((item, path + [index]))
|
||||
}
|
||||
}
|
||||
}
|
||||
var screenshot: Any = NSNull()
|
||||
if request.screenshot {
|
||||
guard #available(macOS 14, *) else { try fail("unsupported-host", "Selected-window screenshots require macOS 14 or later") }
|
||||
let content = try await SCShareableContent.excludingDesktopWindows(true, onScreenWindowsOnly: false)
|
||||
guard let scWindow = content.windows.first(where: { $0.windowID == request.window_id && $0.owningApplication?.processID == request.pid }) else {
|
||||
try fail("capture-window-missing", "Selected window disappeared before capture")
|
||||
}
|
||||
guard bounds.width > 0 && bounds.height > 0 && bounds.width <= 100000 && bounds.height <= 100000 else { try fail("window-bounds", "Selected window has invalid or oversized geometry") }
|
||||
let filter = SCContentFilter(desktopIndependentWindow: scWindow)
|
||||
let configuration = SCStreamConfiguration()
|
||||
let scale = min(1, 2048 / max(bounds.width, bounds.height))
|
||||
configuration.width = max(1, Int(bounds.width * scale)); configuration.height = max(1, Int(bounds.height * scale))
|
||||
configuration.showsCursor = false
|
||||
let image = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: configuration)
|
||||
guard let png = NSBitmapImageRep(cgImage: image).representation(using: .png, properties: [:]) else {
|
||||
try fail("capture-failed", "Selected-window PNG encoding failed")
|
||||
}
|
||||
screenshot = ["mime_type": "image/png", "base64": png.base64EncodedString(), "sha256": SHA256.hash(data: png).map({ String(format: "%02x", $0) }).joined(), "width": image.width, "height": image.height]
|
||||
}
|
||||
return ["window": ["pid": request.pid, "window_id": request.window_id, "executable": request.executable, "launch_time": launch, "title": window[kCGWindowName as String] as? String ?? ""], "nodes": nodes, "truncated": truncated, "screenshot": screenshot, "gaps": request.accessibility ? gaps : ["Accessibility capture was disabled"]]
|
||||
}
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let request = try JSONDecoder().decode(Request.self, from: Data(CommandLine.arguments[1].utf8))
|
||||
let result = try await observe(request)
|
||||
let output = try serializeHelperOutput(["ok": true, "result": result])
|
||||
FileHandle.standardOutput.write(output)
|
||||
} catch {
|
||||
let failure = error as? BoundaryFailure
|
||||
let output: [String: Any] = ["ok": false, "code": failure?.code ?? "capture-failed", "message": failure?.message ?? error.localizedDescription]
|
||||
if let data = try? serializeHelperOutput(output) { FileHandle.standardOutput.write(data) }
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
RunLoop.main.run()
|
||||
@@ -1,303 +0,0 @@
|
||||
"""Bounded native call observation executed inside LLDB's embedded Python.
|
||||
|
||||
REA imports this module into `lldb --batch` and runs `rea_trace <config.json>`.
|
||||
It launches one owned process, stops at the requested functions and
|
||||
Objective-C methods, records each entry from registers and memory reads only,
|
||||
continues, and kills the process once it exits, the event limit is reached or
|
||||
the duration elapses. No expression is evaluated and no target code runs on
|
||||
the tracer's behalf. The result is written as JSON to the configured path.
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
import lldb
|
||||
|
||||
ARGUMENT_REGISTERS = {
|
||||
"arm64": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
|
||||
"arm64e": ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7"],
|
||||
"x86_64": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
|
||||
"x86_64h": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
|
||||
}
|
||||
MAX_SELECTOR_BYTES = 1024
|
||||
MAX_REPORTED_LOCATIONS = 64
|
||||
MAX_OTHER_STOPS = 256
|
||||
LISTENER_NAME = "rea-native-call-observation"
|
||||
|
||||
|
||||
def _hex(value):
|
||||
return "0x%x" % value
|
||||
|
||||
|
||||
def _location(address, target):
|
||||
"""Load and file address, module and symbol of one code address."""
|
||||
module = address.GetModule()
|
||||
symbol = address.GetSymbol()
|
||||
file_address = address.GetFileAddress()
|
||||
return {
|
||||
"load_address": _hex(address.GetLoadAddress(target)),
|
||||
"file_address": None
|
||||
if file_address == lldb.LLDB_INVALID_ADDRESS
|
||||
else _hex(file_address),
|
||||
"module": module.GetFileSpec().GetFilename() if module.IsValid() else None,
|
||||
"module_path": str(module.GetFileSpec()) if module.IsValid() else None,
|
||||
"symbol": symbol.GetName() if symbol.IsValid() else None,
|
||||
}
|
||||
|
||||
|
||||
def _no_files():
|
||||
return lldb.SBFileSpecList()
|
||||
|
||||
|
||||
def _create_breakpoints(target, spec):
|
||||
"""LLDB breakpoints for one request; a class-qualified method may need two."""
|
||||
if spec["kind"] == "function":
|
||||
modules = lldb.SBFileSpecList()
|
||||
if spec.get("module"):
|
||||
modules.Append(lldb.SBFileSpec(spec["module"], False))
|
||||
return [
|
||||
target.BreakpointCreateByName(
|
||||
spec["name"], lldb.eFunctionNameTypeAuto, modules, _no_files()
|
||||
)
|
||||
]
|
||||
class_name = spec.get("class_name")
|
||||
method_type = spec.get("method_type", "any")
|
||||
if class_name is None:
|
||||
return [
|
||||
target.BreakpointCreateByName(
|
||||
spec["selector"], lldb.eFunctionNameTypeSelector, _no_files(), _no_files()
|
||||
)
|
||||
]
|
||||
prefixes = {"instance": ["-"], "class": ["+"], "any": ["-", "+"]}[method_type]
|
||||
return [
|
||||
target.BreakpointCreateByName(
|
||||
"%s[%s %s]" % (prefix, class_name, spec["selector"]),
|
||||
lldb.eFunctionNameTypeFull,
|
||||
_no_files(),
|
||||
_no_files(),
|
||||
)
|
||||
for prefix in prefixes
|
||||
]
|
||||
|
||||
|
||||
def _dynamic_class(target, pointer):
|
||||
"""Class of an Objective-C receiver from LLDB's runtime reader, without running code."""
|
||||
if pointer == 0:
|
||||
return None
|
||||
data = lldb.SBData.CreateDataFromUInt64Array(
|
||||
target.GetByteOrder(), target.GetAddressByteSize(), [pointer]
|
||||
)
|
||||
value = target.CreateValueFromData(
|
||||
"receiver", data, target.GetBasicType(lldb.eBasicTypeObjCID)
|
||||
)
|
||||
dynamic = value.GetDynamicValue(lldb.eDynamicDontRunTarget)
|
||||
name = dynamic.GetTypeName() if dynamic.IsValid() else None
|
||||
if not name or name in ("id", "objc_object *"):
|
||||
return None
|
||||
return name[:-2] if name.endswith(" *") else name
|
||||
|
||||
|
||||
def _wanted(spec, symbol):
|
||||
"""Selector-only requests may admit only instance or only class methods."""
|
||||
if spec["kind"] != "objc-method" or spec.get("class_name") is not None:
|
||||
return True
|
||||
method_type = spec.get("method_type", "any")
|
||||
if method_type == "any":
|
||||
return True
|
||||
return symbol.startswith("-[" if method_type == "instance" else "+[")
|
||||
|
||||
|
||||
def _record(context, thread, index, sequence):
|
||||
target, process, config = context["target"], context["process"], context["config"]
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
location = _location(frame.GetPCAddress(), target)
|
||||
symbol = location["symbol"] or ""
|
||||
if not _wanted(config["breakpoints"][index], symbol):
|
||||
return None
|
||||
names = ARGUMENT_REGISTERS.get(config["architecture"], [])
|
||||
registers = [
|
||||
{"name": name, "value": _hex(frame.FindRegister(name).GetValueAsUnsigned())}
|
||||
for name in names[: config["argument_registers"]]
|
||||
]
|
||||
receiver_class = None
|
||||
selector = None
|
||||
if symbol.startswith("-[") or symbol.startswith("+["):
|
||||
error = lldb.SBError()
|
||||
text = process.ReadCStringFromMemory(
|
||||
frame.FindRegister(names[1]).GetValueAsUnsigned(),
|
||||
MAX_SELECTOR_BYTES,
|
||||
error,
|
||||
)
|
||||
selector = text if error.Success() and text else None
|
||||
if symbol.startswith("-["):
|
||||
receiver_class = _dynamic_class(
|
||||
target, frame.FindRegister(names[0]).GetValueAsUnsigned()
|
||||
)
|
||||
frames = min(thread.GetNumFrames(), 1 + config["backtrace_frames"])
|
||||
record = {
|
||||
"sequence": sequence,
|
||||
"elapsed_ms": round((time.monotonic() - context["started"]) * 1000, 3),
|
||||
"thread_id": thread.GetThreadID(),
|
||||
"breakpoint_index": index,
|
||||
"receiver_class": receiver_class,
|
||||
"selector": selector,
|
||||
"registers": registers,
|
||||
"backtrace": [
|
||||
_location(thread.GetFrameAtIndex(depth).GetPCAddress(), target)
|
||||
for depth in range(1, frames)
|
||||
],
|
||||
}
|
||||
record.update(location)
|
||||
return record
|
||||
|
||||
|
||||
def _handle_stop(context, events, other_stops):
|
||||
"""Record breakpoint hits of one stop; return an outcome when tracing must end."""
|
||||
process = context["process"]
|
||||
for thread in process:
|
||||
reason = thread.GetStopReason()
|
||||
if reason == lldb.eStopReasonBreakpoint:
|
||||
index = context["breakpoint_index"].get(thread.GetStopReasonDataAtIndex(0))
|
||||
if index is None:
|
||||
continue
|
||||
record = _record(context, thread, index, len(events))
|
||||
if record is not None:
|
||||
events.append(record)
|
||||
if len(events) >= context["config"]["max_events"]:
|
||||
return "event-limit"
|
||||
elif reason in (lldb.eStopReasonSignal, lldb.eStopReasonException):
|
||||
other_stops.append(thread.GetStopDescription(256))
|
||||
if len(other_stops) >= MAX_OTHER_STOPS:
|
||||
return "stop-limit"
|
||||
return None
|
||||
|
||||
|
||||
def _breakpoint_report(target, created):
|
||||
report = []
|
||||
for index, breakpoints in enumerate(created):
|
||||
locations = []
|
||||
count = 0
|
||||
for breakpoint in breakpoints:
|
||||
count += breakpoint.GetNumLocations()
|
||||
for position in range(breakpoint.GetNumLocations()):
|
||||
if len(locations) < MAX_REPORTED_LOCATIONS:
|
||||
locations.append(
|
||||
_location(breakpoint.GetLocationAtIndex(position).GetAddress(), target)
|
||||
)
|
||||
report.append({"index": index, "location_count": count, "locations": locations})
|
||||
return report
|
||||
|
||||
|
||||
def _launch_info(target, config, listener):
|
||||
launch = lldb.SBLaunchInfo(config["arguments"])
|
||||
launch.SetListener(listener)
|
||||
environment = target.GetEnvironment()
|
||||
for key, value in config["environment"].items():
|
||||
environment.Set(key, value, True)
|
||||
launch.SetEnvironment(environment, False)
|
||||
if config.get("working_directory"):
|
||||
launch.SetWorkingDirectory(config["working_directory"])
|
||||
launch.AddOpenFileAction(0, "/dev/null", True, False)
|
||||
launch.AddOpenFileAction(1, config["stdout_path"], False, True)
|
||||
launch.AddOpenFileAction(2, config["stderr_path"], False, True)
|
||||
return launch
|
||||
|
||||
|
||||
def _wait_for_exit(listener, process, seconds):
|
||||
event = lldb.SBEvent()
|
||||
deadline = time.monotonic() + seconds
|
||||
while process.GetState() != lldb.eStateExited and time.monotonic() < deadline:
|
||||
listener.WaitForEvent(1, event)
|
||||
|
||||
|
||||
def trace(debugger, config):
|
||||
debugger.SetAsync(True)
|
||||
# Stop at the symbol itself, where argument registers still hold the ABI
|
||||
# arguments, even when line tables would let LLDB skip the prologue.
|
||||
lldb.SBDebugger.SetInternalVariable(
|
||||
"target.skip-prologue", "false", debugger.GetInstanceName()
|
||||
)
|
||||
error = lldb.SBError()
|
||||
target = debugger.CreateTarget(
|
||||
config["executable"], config["architecture"], None, False, error
|
||||
)
|
||||
if not target.IsValid():
|
||||
return {"status": "target-error", "error": error.GetCString()}
|
||||
created = [_create_breakpoints(target, spec) for spec in config["breakpoints"]]
|
||||
breakpoint_index = {}
|
||||
for index, breakpoints in enumerate(created):
|
||||
for breakpoint in breakpoints:
|
||||
breakpoint_index[breakpoint.GetID()] = index
|
||||
listener = lldb.SBListener(LISTENER_NAME)
|
||||
started = time.monotonic()
|
||||
process = target.Launch(_launch_info(target, config, listener), error)
|
||||
if not error.Success() or not process.IsValid():
|
||||
return {"status": "launch-error", "error": error.GetCString()}
|
||||
with open(config["pid_path"], "w") as handle:
|
||||
handle.write(str(process.GetProcessID()))
|
||||
context = {
|
||||
"target": target,
|
||||
"process": process,
|
||||
"config": config,
|
||||
"started": started,
|
||||
"breakpoint_index": breakpoint_index,
|
||||
}
|
||||
events = []
|
||||
other_stops = []
|
||||
outcome = None
|
||||
deadline = started + config["duration_ms"] / 1000
|
||||
event = lldb.SBEvent()
|
||||
while outcome is None:
|
||||
if time.monotonic() >= deadline:
|
||||
outcome = "duration-elapsed"
|
||||
break
|
||||
if not listener.WaitForEvent(1, event):
|
||||
continue
|
||||
if not lldb.SBProcess.EventIsProcessEvent(event):
|
||||
continue
|
||||
state = lldb.SBProcess.GetStateFromEvent(event)
|
||||
if state == lldb.eStateExited:
|
||||
outcome = "exited"
|
||||
elif state == lldb.eStateStopped and not lldb.SBProcess.GetRestartedFromEvent(event):
|
||||
outcome = _handle_stop(context, events, other_stops)
|
||||
if outcome is None:
|
||||
process.Continue()
|
||||
pid = process.GetProcessID()
|
||||
exit_status = None
|
||||
exit_description = None
|
||||
if outcome == "exited":
|
||||
exit_status = process.GetExitStatus()
|
||||
exit_description = process.GetExitDescription()
|
||||
else:
|
||||
process.Kill()
|
||||
_wait_for_exit(listener, process, 5)
|
||||
return {
|
||||
"status": "traced",
|
||||
"version": lldb.SBDebugger.GetVersionString().splitlines()[0],
|
||||
"pid": pid,
|
||||
"outcome": outcome,
|
||||
"exit_status": exit_status,
|
||||
"exit_description": exit_description,
|
||||
"killed": outcome != "exited",
|
||||
"terminated": process.GetState() == lldb.eStateExited,
|
||||
"elapsed_ms": round((time.monotonic() - started) * 1000, 3),
|
||||
"breakpoints": _breakpoint_report(target, created),
|
||||
"events": events,
|
||||
"other_stops": other_stops,
|
||||
}
|
||||
|
||||
|
||||
def run(debugger, command, result, internal_dict):
|
||||
"""LLDB command entry point: `rea_trace <config.json>`."""
|
||||
with open(command.strip()) as handle:
|
||||
config = json.load(handle)
|
||||
try:
|
||||
output = trace(debugger, config)
|
||||
except Exception as error: # Reported as a tracer failure, never re-raised into LLDB.
|
||||
output = {"status": "tracer-error", "error": "%s: %s" % (type(error).__name__, error)}
|
||||
with open(config["result_path"], "w") as handle:
|
||||
json.dump(output, handle)
|
||||
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
debugger.HandleCommand("command script add -f rea_lldb_tracer.run rea_trace")
|
||||
@@ -1,293 +0,0 @@
|
||||
import Darwin
|
||||
import Foundation
|
||||
|
||||
struct RunTokenObservation: Encodable {
|
||||
let pid: Int32
|
||||
let state: String
|
||||
let run_id: String?
|
||||
let reason: String?
|
||||
}
|
||||
|
||||
struct ProcessIdentityObservation: Encodable {
|
||||
let pid: Int32
|
||||
let state: String
|
||||
let identity: String?
|
||||
let reason: String?
|
||||
}
|
||||
|
||||
private struct ProcessMetadata {
|
||||
let identity: String
|
||||
let pointerSize: Int
|
||||
}
|
||||
|
||||
struct ProcessSnapshot {
|
||||
fileprivate let metadata: [Int32: ProcessMetadata]
|
||||
let failure: String?
|
||||
}
|
||||
|
||||
typealias ProcessTableSysctlCall = (
|
||||
_ mib: inout [Int32],
|
||||
_ output: UnsafeMutableRawPointer?,
|
||||
_ size: inout Int
|
||||
) -> Int32
|
||||
|
||||
private func readProcessSnapshot() -> ProcessSnapshot {
|
||||
readProcessSnapshot { mib, output, size in
|
||||
sysctl(&mib, u_int(mib.count), output, &size, nil, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func readProcessSnapshot(
|
||||
sysctlCall: ProcessTableSysctlCall
|
||||
) -> ProcessSnapshot {
|
||||
for _ in 0..<3 {
|
||||
var mib = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0]
|
||||
var size = 0
|
||||
guard sysctlCall(&mib, nil, &size) == 0 else {
|
||||
let failure = errno
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(failure)")
|
||||
}
|
||||
guard size >= MemoryLayout<kinfo_proc>.size,
|
||||
size % MemoryLayout<kinfo_proc>.stride == 0 else {
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(errno)")
|
||||
}
|
||||
var processes = [kinfo_proc](
|
||||
repeating: kinfo_proc(),
|
||||
count: size / MemoryLayout<kinfo_proc>.stride
|
||||
)
|
||||
let result = processes.withUnsafeMutableBufferPointer { buffer in
|
||||
let output = buffer.baseAddress.map { UnsafeMutableRawPointer($0) }
|
||||
return sysctlCall(&mib, output, &size)
|
||||
}
|
||||
guard result == 0 else {
|
||||
let failure = errno
|
||||
if failure == ENOMEM { continue }
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(failure)")
|
||||
}
|
||||
guard size % MemoryLayout<kinfo_proc>.stride == 0 else {
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(errno)")
|
||||
}
|
||||
let processCount = size / MemoryLayout<kinfo_proc>.stride
|
||||
let metadata = Dictionary(
|
||||
processes.prefix(processCount).map { process in
|
||||
let start = process.kp_proc.p_starttime
|
||||
return (
|
||||
process.kp_proc.p_pid,
|
||||
ProcessMetadata(
|
||||
identity: "\(start.tv_sec):\(start.tv_usec)",
|
||||
pointerSize: process.kp_proc.p_flag & P_LP64 != 0 ? 8 : 4
|
||||
)
|
||||
)
|
||||
},
|
||||
uniquingKeysWith: { first, _ in first }
|
||||
)
|
||||
return ProcessSnapshot(metadata: metadata, failure: nil)
|
||||
}
|
||||
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(ENOMEM)")
|
||||
}
|
||||
|
||||
func readProcessIdentities(pids: [Int32]) -> [ProcessIdentityObservation] {
|
||||
let snapshot = readProcessSnapshot()
|
||||
return pids.map { pid in
|
||||
guard let metadata = snapshot.metadata[pid] else {
|
||||
return unavailableIdentity(
|
||||
pid: pid,
|
||||
reason: snapshot.failure ?? "process_not_in_snapshot"
|
||||
)
|
||||
}
|
||||
return ProcessIdentityObservation(pid: pid, state: "readable", identity: metadata.identity, reason: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func unavailableIdentity(pid: Int32, reason: String) -> ProcessIdentityObservation {
|
||||
ProcessIdentityObservation(pid: pid, state: "unavailable", identity: nil, reason: reason)
|
||||
}
|
||||
|
||||
enum RunTokenReadError: Error {
|
||||
case invalidBuffer
|
||||
case duplicateToken
|
||||
case invalidTokenEncoding
|
||||
case environmentUnavailable
|
||||
case appleVectorUnavailable
|
||||
case ambiguousEnvironmentBoundary
|
||||
}
|
||||
|
||||
@_silgen_name("csops")
|
||||
private func csops(
|
||||
_ pid: Int32,
|
||||
_ ops: UInt32,
|
||||
_ addr: UnsafeMutableRawPointer?,
|
||||
_ size: Int
|
||||
) -> Int32
|
||||
|
||||
private let csOpsStatus: UInt32 = 0
|
||||
private let csPlatformBinary: UInt32 = 0x0400_0000
|
||||
|
||||
/**
|
||||
* Whether the target image is an Apple platform binary. Current macOS omits
|
||||
* the environment of platform binaries from KERN_PROCARGS2 for every caller,
|
||||
* so this distinguishes a policy-withheld environment from a process whose
|
||||
* environment is genuinely absent or transiently unreadable.
|
||||
*/
|
||||
func isPlatformBinary(pid: Int32) -> Bool {
|
||||
var flags: UInt32 = 0
|
||||
let result = withUnsafeMutablePointer(to: &flags) { pointer in
|
||||
csops(pid, csOpsStatus, UnsafeMutableRawPointer(pointer), MemoryLayout<UInt32>.size)
|
||||
}
|
||||
return result == 0 && (flags & csPlatformBinary) != 0
|
||||
}
|
||||
|
||||
private func readRunToken(pid: Int32, pointerSize: Int) -> RunTokenObservation {
|
||||
var mib = [CTL_KERN, KERN_PROCARGS2, Int32(pid)]
|
||||
var size = 0
|
||||
guard sysctl(&mib, u_int(mib.count), nil, &size, nil, 0) == 0, size >= MemoryLayout<Int32>.size else {
|
||||
return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
|
||||
}
|
||||
var bytes = [UInt8](repeating: 0, count: size)
|
||||
let result = bytes.withUnsafeMutableBytes { buffer in
|
||||
sysctl(&mib, u_int(mib.count), buffer.baseAddress, &size, nil, 0)
|
||||
}
|
||||
guard result == 0 else {
|
||||
return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
|
||||
}
|
||||
bytes = Array(bytes.prefix(size))
|
||||
do {
|
||||
return RunTokenObservation(pid: pid, state: "readable", run_id: try token(in: bytes, pointerSize: pointerSize), reason: nil)
|
||||
} catch RunTokenReadError.duplicateToken {
|
||||
return unavailable(pid: pid, reason: "duplicate_run_token")
|
||||
} catch RunTokenReadError.invalidTokenEncoding {
|
||||
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
|
||||
} catch RunTokenReadError.environmentUnavailable {
|
||||
return unavailable(
|
||||
pid: pid,
|
||||
reason: isPlatformBinary(pid: pid)
|
||||
? "platform_binary_environment_withheld"
|
||||
: "environment_unavailable"
|
||||
)
|
||||
} catch RunTokenReadError.appleVectorUnavailable {
|
||||
return unavailable(pid: pid, reason: "apple_vector_unavailable")
|
||||
} catch RunTokenReadError.ambiguousEnvironmentBoundary {
|
||||
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
|
||||
} catch {
|
||||
return unavailable(pid: pid, reason: "malformed_procargs")
|
||||
}
|
||||
}
|
||||
|
||||
func readRunTokens(pids: [Int32]) -> [RunTokenObservation] {
|
||||
let before = readProcessSnapshot()
|
||||
let observations = pids.map { pid -> RunTokenObservation in
|
||||
guard let metadata = before.metadata[pid] else {
|
||||
return unavailable(pid: pid, reason: before.failure ?? "process_not_in_snapshot")
|
||||
}
|
||||
return readRunToken(pid: pid, pointerSize: metadata.pointerSize)
|
||||
}
|
||||
let after = readProcessSnapshot()
|
||||
return zip(pids, observations).map { pid, observation in
|
||||
guard let initial = before.metadata[pid], let current = after.metadata[pid] else {
|
||||
return unavailable(pid: pid, reason: after.failure ?? "process_not_in_snapshot")
|
||||
}
|
||||
guard initial.identity == current.identity,
|
||||
initial.pointerSize == current.pointerSize else {
|
||||
return unavailable(pid: pid, reason: "process_identity_changed_during_token_read")
|
||||
}
|
||||
return observation
|
||||
}
|
||||
}
|
||||
|
||||
func token(in bytes: [UInt8], pointerSize: Int = MemoryLayout<UnsafeRawPointer>.size) throws -> String? {
|
||||
guard bytes.count >= MemoryLayout<Int32>.size else { throw RunTokenReadError.invalidBuffer }
|
||||
let argc = bytes.withUnsafeBytes { $0.loadUnaligned(as: Int32.self).littleEndian }
|
||||
guard argc >= 0, Int(argc) <= bytes.count else { throw RunTokenReadError.invalidBuffer }
|
||||
var offset = MemoryLayout<Int32>.size
|
||||
guard let executablePath = readString(in: bytes, offset: &offset) else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
guard pointerSize == 4 || pointerSize == 8 else { throw RunTokenReadError.invalidBuffer }
|
||||
let pathRecordSize = Array("executable_path=".utf8).count + executablePath.count + 1
|
||||
let padding = (pointerSize - pathRecordSize % pointerSize) % pointerSize
|
||||
guard offset + padding <= bytes.count,
|
||||
bytes[offset..<(offset + padding)].allSatisfy({ $0 == 0 }) else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
offset += padding
|
||||
for _ in 0..<argc {
|
||||
guard readString(in: bytes, offset: &offset, allowEmpty: true) != nil else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
}
|
||||
guard bytes.last == 0 else { throw RunTokenReadError.invalidBuffer }
|
||||
if offset == bytes.count {
|
||||
throw RunTokenReadError.environmentUnavailable
|
||||
}
|
||||
var trailingEntries: [ArraySlice<UInt8>] = []
|
||||
var zeroedAppleVectorStart: Int?
|
||||
while offset < bytes.count {
|
||||
if bytes[offset] == 0 {
|
||||
let start = offset
|
||||
while offset < bytes.count, bytes[offset] == 0 {
|
||||
offset += 1
|
||||
}
|
||||
if offset - start > 1 {
|
||||
zeroedAppleVectorStart = zeroedAppleVectorStart ?? trailingEntries.count
|
||||
}
|
||||
continue
|
||||
}
|
||||
guard let entry = readString(in: bytes, offset: &offset) else {
|
||||
throw RunTokenReadError.invalidBuffer
|
||||
}
|
||||
trailingEntries.append(entry)
|
||||
}
|
||||
// XNU appends its Apple vector after the environment. libc clears the
|
||||
// initial `pfz=` record after startup, leaving a multi-NUL run in its place.
|
||||
// A caller can also set `pfz`, so prefer the final intact sentinel when it
|
||||
// remains available and otherwise stop at the cleared record.
|
||||
let intactAppleVectorStart = trailingEntries.lastIndex(where: {
|
||||
$0.starts(with: Array("pfz=".utf8))
|
||||
})
|
||||
let preferZeroedBoundary = zeroedAppleVectorStart.flatMap { zeroedStart in
|
||||
intactAppleVectorStart.map { intactStart in intactStart < zeroedStart } ?? true
|
||||
} ?? false
|
||||
let appleVectorStart = preferZeroedBoundary
|
||||
? zeroedAppleVectorStart
|
||||
: intactAppleVectorStart
|
||||
guard let appleVectorStart else {
|
||||
throw RunTokenReadError.appleVectorUnavailable
|
||||
}
|
||||
if trailingEntries[appleVectorStart...].contains(where: {
|
||||
$0.starts(with: Array("REA_PROCESS_RUN_ID=".utf8))
|
||||
}) {
|
||||
throw RunTokenReadError.ambiguousEnvironmentBoundary
|
||||
}
|
||||
let environmentEntries = trailingEntries[..<appleVectorStart]
|
||||
guard !environmentEntries.isEmpty else {
|
||||
throw RunTokenReadError.environmentUnavailable
|
||||
}
|
||||
var found: String?
|
||||
for entry in environmentEntries {
|
||||
if entry.starts(with: Array("REA_PROCESS_RUN_ID=".utf8)) {
|
||||
guard found == nil else { throw RunTokenReadError.duplicateToken }
|
||||
let value = entry.dropFirst("REA_PROCESS_RUN_ID=".utf8.count)
|
||||
guard let decoded = String(bytes: value, encoding: .utf8) else {
|
||||
throw RunTokenReadError.invalidTokenEncoding
|
||||
}
|
||||
found = decoded
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
private func readString(
|
||||
in bytes: [UInt8],
|
||||
offset: inout Int,
|
||||
allowEmpty: Bool = false
|
||||
) -> ArraySlice<UInt8>? {
|
||||
guard offset < bytes.count, let end = bytes[offset...].firstIndex(of: 0),
|
||||
allowEmpty || end > offset else { return nil }
|
||||
let string = bytes[offset..<end]
|
||||
offset = end + 1
|
||||
return string
|
||||
}
|
||||
|
||||
private func unavailable(pid: Int32, reason: String) -> RunTokenObservation {
|
||||
RunTokenObservation(pid: pid, state: "unavailable", run_id: nil, reason: reason)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
struct RunTokenResponse: Encodable {
|
||||
let results: [RunTokenObservation]
|
||||
}
|
||||
|
||||
struct ProcessIdentityResponse: Encodable {
|
||||
let results: [ProcessIdentityObservation]
|
||||
}
|
||||
|
||||
@main
|
||||
struct ProcessRunTokenReaderMain {
|
||||
static func main() {
|
||||
let arguments = Array(CommandLine.arguments.dropFirst())
|
||||
let identityMode = arguments.first == "--identities"
|
||||
let pidArguments = identityMode ? Array(arguments.dropFirst()) : arguments
|
||||
let pids = pidArguments.compactMap(Int32.init)
|
||||
guard pids.count == pidArguments.count else {
|
||||
fputs("invalid process ID input\n", stderr)
|
||||
exit(64)
|
||||
}
|
||||
do {
|
||||
let output: Data
|
||||
if identityMode {
|
||||
output = try JSONEncoder().encode(ProcessIdentityResponse(results: readProcessIdentities(pids: pids)))
|
||||
} else {
|
||||
output = try JSONEncoder().encode(RunTokenResponse(results: readRunTokens(pids: pids)))
|
||||
}
|
||||
FileHandle.standardOutput.write(output)
|
||||
} catch {
|
||||
fputs("could not encode process token observations\n", stderr)
|
||||
exit(70)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
"""Fixed core-only GDB/pwndbg operation; no caller commands or historical PID lookup."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import gdb
|
||||
|
||||
OUTPUT_BYTES = 64 * 1024 * 1024
|
||||
|
||||
|
||||
class ContextFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def inspect(request):
|
||||
if gdb.selected_inferior().connection is not None:
|
||||
raise ContextFailure("decoder", "Owned GDB unexpectedly has an inferior connection before core loading.")
|
||||
root = Path(os.environ["REA_CORE_CONTEXT_REQUEST"]).parent
|
||||
empty = root / "no-libraries"
|
||||
empty.mkdir(mode=0o700)
|
||||
for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off",
|
||||
"set sysroot ./no-libraries", "set solib-search-path ./no-libraries"]:
|
||||
gdb.execute(command, to_string=True)
|
||||
entry = request["gdbinit"]
|
||||
try:
|
||||
exec(compile(Path(entry).read_bytes(), entry, "exec"), {"__name__": "__main__", "__file__": entry})
|
||||
import pwndbg.aglib.vmmap
|
||||
import pwndbg.lib.version
|
||||
except (ImportError, OSError) as error:
|
||||
raise ContextFailure("unavailable", "Selected pwndbg could not initialize: " + str(error)) from error
|
||||
version = pwndbg.lib.version.__version__
|
||||
if version != "2026.09.15" and not version.startswith("2026.09.15 build: "):
|
||||
raise ContextFailure("unsupported", "Expected unchanged pwndbg2026.09.15; selected plugin reports " + version + ".")
|
||||
gdb.execute('set context-sections ""', to_string=True)
|
||||
# core-file takes a raw filename and retains quote characters. Use a fixed
|
||||
# relative filename in the owned cwd so no selected path becomes GDB syntax.
|
||||
if Path(request["snapshot_path"]) != root.parent / "object.snapshot":
|
||||
raise ContextFailure("decoder", "Core context request did not select the shared owned snapshot.")
|
||||
gdb.execute("core-file ../object.snapshot", to_string=True)
|
||||
inferior = gdb.selected_inferior()
|
||||
if inferior.connection is None or inferior.connection.type != "core" or gdb.current_progspace().filename is not None:
|
||||
raise ContextFailure("decoder", "Requested core-only boundary was not established; no mapping query performed.")
|
||||
maps = []
|
||||
for page in pwndbg.aglib.vmmap.get():
|
||||
flags = page.flags
|
||||
maps.append({"start": hex(page.start), "end": hex(page.end), "offset": hex(page.offset), "reported_flags": flags,
|
||||
"permissions": {"read": bool(flags & 4), "write": bool(flags & 2), "execute": bool(flags & 1)} if 1 <= flags <= 7 else None,
|
||||
"pathname_display": page.objfile, "current_file_identity": "unknown"})
|
||||
return {"status": "available", "gdb_version": gdb.VERSION, "pwndbg_version": version, "connection": "core", "executable": None,
|
||||
"historical_pid": inferior.pid, "reported_limits": {"address_space_bytes": resource.getrlimit(resource.RLIMIT_AS)[0], "cpu_seconds": resource.getrlimit(resource.RLIMIT_CPU)[0], "file_size_bytes": resource.getrlimit(resource.RLIMIT_FSIZE)[0]}, "maps": maps, "confidence": "derived", "completeness": "unknown",
|
||||
"diagnostics": {"stdout": "", "stderr": "", "truncated": False}}
|
||||
|
||||
|
||||
def failure_exit(request_path, status, marker):
|
||||
try:
|
||||
descriptor = os.open(Path(request_path).parent / "resource.failure", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
|
||||
def main():
|
||||
request_path = os.environ["REA_CORE_CONTEXT_REQUEST"]
|
||||
request = json.loads(Path(request_path).read_text(encoding="utf-8"))
|
||||
reserve = bytearray(1024 * 1024)
|
||||
try:
|
||||
reply = {"ok": True, "value": inspect(request)}
|
||||
except ContextFailure as error:
|
||||
reply = {"ok": False, "reason": error.reason, "message": str(error)}
|
||||
except MemoryError:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
except Exception as error:
|
||||
if isinstance(error, OSError) and error.errno == 12:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
reply = {"ok": False, "reason": "decoder", "message": type(error).__name__ + ": " + str(error)}
|
||||
try:
|
||||
encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
|
||||
if len(encoded) > OUTPUT_BYTES:
|
||||
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds64 MiB reply budget."}).encode("utf-8")
|
||||
descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "wb") as handle:
|
||||
handle.write(encoded)
|
||||
except MemoryError:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
except OSError as error:
|
||||
if error.errno == 27:
|
||||
failure_exit(request_path, 76, b"F")
|
||||
if error.errno == 12:
|
||||
reserve = None
|
||||
failure_exit(request_path, 75, b"M")
|
||||
raise
|
||||
|
||||
|
||||
main()
|
||||
@@ -1,39 +0,0 @@
|
||||
"""Catch typed resource failures while loading or running the owned debugger launcher.
|
||||
|
||||
Only this package's fixed sibling is executed; caller input stays in request.json.
|
||||
Interpreter startup failures before this boundary remain unclassified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def resource_exit(status, marker):
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
# Without a complete marker the parent keeps this failure unclassified.
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
try:
|
||||
implementation = os.path.join(os.path.dirname(__file__), "launch_impl.py")
|
||||
with open(implementation, "rb") as handle:
|
||||
source = handle.read()
|
||||
exec(compile(source, implementation, "exec"), {
|
||||
"__file__": implementation,
|
||||
"__name__": "__main__",
|
||||
})
|
||||
except MemoryError:
|
||||
# Reserved status: an observed allocation failure, including imports/compile.
|
||||
resource_exit(75, b"M")
|
||||
except OSError as error:
|
||||
# Linux ENOMEM is an observed allocation failure, independent of error text.
|
||||
if error.errno == 12:
|
||||
resource_exit(75, b"M")
|
||||
# Linux EFBIG is observed directly, including a partial limits/reply write.
|
||||
if error.errno == 27:
|
||||
resource_exit(76, b"F")
|
||||
raise
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Lower inherited limits before replacing this owned process with GDB."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
runtime_path = Path(__file__).parent.parent / "pwntools" / "decoder_runtime.py"
|
||||
runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
|
||||
exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
|
||||
|
||||
|
||||
class LaunchFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def launch(snapshot, cache):
|
||||
request_path = sys.argv[-1]
|
||||
request = json.loads(Path(request_path).read_text(encoding="utf-8"))
|
||||
bridge = str(Path(__file__).with_name("core_context.py"))
|
||||
environment = dict(os.environ, REA_CORE_CONTEXT_REQUEST=request_path)
|
||||
# Only fixed package code appears in the GDB Python command. Selected paths stay JSON data.
|
||||
command = "python exec(compile(open(" + repr(bridge) + ", 'rb').read(), " + repr(bridge) + ", 'exec'), {'__name__':'__main__','__file__':" + repr(bridge) + "})"
|
||||
try:
|
||||
os.execve(request["gdb"], [request["gdb"], "-nx", "-nh", "--batch", "--quiet",
|
||||
"-iex", "set auto-load off", "-iex", "set debuginfod enabled off",
|
||||
"-iex", "set auto-solib-add off", "-ex", command], environment)
|
||||
except OSError as error:
|
||||
raise LaunchFailure("unavailable", "Selected GDB could not start (" + str(error.errno) + "): " + request["gdb"] + "; " + str(error)) from error
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
runtime["main"](sys.argv[-1], launch, LaunchFailure, "pwndbg-core-launcher")
|
||||
@@ -1,59 +0,0 @@
|
||||
"""Shared bounded reply/limit protocol, independent of the supplied artifact format."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import sys
|
||||
|
||||
OUTPUT_BYTES = 64 * 1024 * 1024
|
||||
|
||||
def lower_resource_limits():
|
||||
limits = {}
|
||||
for name, kind, maximum in (
|
||||
("address_space_bytes", resource.RLIMIT_AS, 3 * 1024**3),
|
||||
("cpu_seconds", resource.RLIMIT_CPU, 30),
|
||||
("file_size_bytes", resource.RLIMIT_FSIZE, OUTPUT_BYTES),
|
||||
):
|
||||
soft, hard = resource.getrlimit(kind)
|
||||
finite = [maximum] + [value for value in (soft, hard) if value != resource.RLIM_INFINITY]
|
||||
effective = min(finite)
|
||||
# Preserve the inherited hard boundary and never raise a caller's soft limit.
|
||||
resource.setrlimit(kind, (effective, hard))
|
||||
limits[name] = effective
|
||||
return limits
|
||||
|
||||
|
||||
def main(request_path, inspect, failure_type, profile):
|
||||
request = json.loads(Path(request_path).read_text(encoding="utf-8"))
|
||||
os.environ["PWNLIB_NOTERM"] = "1"
|
||||
os.environ["PWNLIB_CACHE_DIR"] = str(Path(request_path).parent / "cache")
|
||||
limits = None
|
||||
# Release bounded emergency headroom before constructing a MemoryError reply.
|
||||
# The outer exit-status contract also covers allocation failure during serialization.
|
||||
memory_reserve = bytearray(1024 * 1024)
|
||||
try:
|
||||
limits = lower_resource_limits()
|
||||
descriptor = os.open(Path(request_path).parent / "limits.json", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
||||
json.dump(limits, handle)
|
||||
value = inspect(Path(request["snapshot_path"]), Path(request_path).parent / "cache")
|
||||
value["limitations"].append("Effective owned Python resource soft limits: " + json.dumps(limits, sort_keys=True) + ". Inherited tighter limits are retained.")
|
||||
reply = {"ok": True, "profile": profile, "value": value}
|
||||
except failure_type as error:
|
||||
reply = {"ok": False, "reason": error.reason, "message": str(error)}
|
||||
except MemoryError:
|
||||
memory_reserve = None
|
||||
reply = {"ok": False, "reason": "resource-limit", "message": "pwntools memory allocation failed under the effective resource limits; the exact allocation cause is unknown.", "reported_limits": limits}
|
||||
except Exception as error:
|
||||
if isinstance(error, OSError) and error.errno == 12:
|
||||
memory_reserve = None
|
||||
reply = {"ok": False, "reason": "resource-limit", "message": "pwntools reported OSError(ENOMEM); the exact allocation cause is unknown.", "reported_limits": limits}
|
||||
else:
|
||||
reply = {"ok": False, "reason": "decoder", "message": type(error).__name__ + ": " + str(error)}
|
||||
encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
|
||||
if len(encoded) > OUTPUT_BYTES:
|
||||
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete artifact result exceeds the 64 MiB reply budget."}).encode("utf-8")
|
||||
descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "wb") as handle:
|
||||
handle.write(encoded)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
"""Catch typed resource failures while loading or running the owned ELF adapter.
|
||||
|
||||
Only this package's fixed sibling is executed; caller input stays in request.json.
|
||||
Interpreter startup failures before this boundary remain unclassified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def resource_exit(status, marker):
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
# Without a complete marker the parent keeps this failure unclassified.
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
try:
|
||||
implementation = os.path.join(os.path.dirname(__file__), "layout_impl.py")
|
||||
with open(implementation, "rb") as handle:
|
||||
source = handle.read()
|
||||
exec(compile(source, implementation, "exec"), {
|
||||
"__file__": implementation,
|
||||
"__name__": "__main__",
|
||||
})
|
||||
except MemoryError:
|
||||
# Reserved status: an observed allocation failure, including imports/compile.
|
||||
resource_exit(75, b"M")
|
||||
except OSError as error:
|
||||
# Linux ENOMEM is an observed allocation failure, independent of error text.
|
||||
if error.errno == 12:
|
||||
resource_exit(75, b"M")
|
||||
# Linux EFBIG is observed directly, including a partial limits/reply write.
|
||||
if error.errno == 27:
|
||||
resource_exit(76, b"F")
|
||||
raise
|
||||
@@ -1,309 +0,0 @@
|
||||
"""Offline adapter to unchanged pwntools 4.15.0; never use libs/maps/libc.
|
||||
|
||||
Owned request/snapshot/reply paths are supplied by REA. No host target-process launch,
|
||||
user configuration initialization, debugger startup, or dependency installation.
|
||||
"""
|
||||
import base64
|
||||
import importlib.metadata
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import resource
|
||||
import sys
|
||||
|
||||
PROFILE = "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2"
|
||||
PACKAGES = {"pwntools": "4.15.0", "pyelftools": "0.33", "unicorn": "2.1.2"}
|
||||
OUTPUT_BYTES = 64 * 1024 * 1024
|
||||
|
||||
|
||||
class LayoutFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def address(value):
|
||||
return hex(int(value))
|
||||
|
||||
|
||||
def file_range(offset, size, length):
|
||||
return 0 <= offset <= length and 0 <= size <= length - offset
|
||||
|
||||
|
||||
def location(offset, size, length):
|
||||
if not file_range(offset, size, length):
|
||||
raise LayoutFailure("format", "ELF contains a file-backed range outside the selected snapshot.")
|
||||
return {"offset": address(offset), "bytes": address(size)}
|
||||
|
||||
|
||||
def name_reference(display, section, offset, content):
|
||||
header = getattr(section, "header", None)
|
||||
if header is None or header.sh_type != "SHT_STRTAB":
|
||||
return {"display": display, "bytes_base64": None, "location": None,
|
||||
"unknown_reason": "Referenced string table could not be resolved."}
|
||||
return name_in_table(display, header.sh_offset, header.sh_size, offset, content)
|
||||
|
||||
|
||||
def name_in_table(display, table_start, table_size, offset, content):
|
||||
location(table_start, table_size, len(content))
|
||||
if not 0 <= offset < table_size:
|
||||
raise LayoutFailure("format", f"ELF name offset {offset} lies outside its declared string table of {table_size} bytes at file offset {table_start}.")
|
||||
start = table_start + offset
|
||||
limit = table_start + table_size
|
||||
end = content.find(b"\0", start, limit)
|
||||
if end < 0:
|
||||
raise LayoutFailure("format", "ELF name lacks a terminator within its reported string table.")
|
||||
raw = content[start:end]
|
||||
return {"display": raw.decode("utf-8", "replace") if display is None else display, "bytes_base64": base64.b64encode(raw).decode("ascii"),
|
||||
"location": location(start, len(raw) + 1, len(content)), "unknown_reason": None}
|
||||
|
||||
|
||||
def dynamic_name_reference(tags, offset, image, content):
|
||||
starts = [tag.d_val for tag in tags if tag.d_tag == "DT_STRTAB"]
|
||||
sizes = [tag.d_val for tag in tags if tag.d_tag == "DT_STRSZ"]
|
||||
if not starts or not sizes:
|
||||
raise LayoutFailure("format", "ELF dependency requires DT_STRTAB and DT_STRSZ.")
|
||||
if len(starts) != 1 or len(sizes) != 1:
|
||||
raise LayoutFailure("unsupported-target", "ELF dependency string-table tags are ambiguous: " + repr({"DT_STRTAB": starts, "DT_STRSZ": sizes}))
|
||||
if not 0 <= offset < sizes[0]:
|
||||
raise LayoutFailure("format", f"ELF dependency name offset {offset} lies outside declared DT_STRSZ {sizes[0]}.")
|
||||
offsets = set()
|
||||
if len(starts) == 1 and len(sizes) == 1:
|
||||
for segment in image.iter_segments_by_type("PT_LOAD"):
|
||||
h = segment.header
|
||||
if starts[0] >= h.p_vaddr and starts[0] + sizes[0] <= h.p_vaddr + h.p_filesz:
|
||||
offsets.add(h.p_offset + starts[0] - h.p_vaddr)
|
||||
if len(offsets) != 1:
|
||||
raise LayoutFailure("unsupported-target", "Dynamic string table lacks a unique file-backed mapping: " + repr({"DT_STRTAB": starts, "DT_STRSZ": sizes, "candidate_file_offsets": sorted(offsets)}))
|
||||
return name_in_table(None, offsets.pop(), sizes[0], offset, content)
|
||||
|
||||
|
||||
def symbol_value_meaning(raw, image_type, sections):
|
||||
index = raw.st_shndx
|
||||
if raw.st_info.type == "STT_FILE": return "no-address"
|
||||
if index == "SHN_UNDEF": return "undefined"
|
||||
if index == "SHN_COMMON": return "alignment"
|
||||
if index == "SHN_ABS": return "absolute-value"
|
||||
if not isinstance(index, int) or not 0 < index < len(sections) or index >= 0xff00:
|
||||
return "unknown-section-index"
|
||||
if sections[index].header.sh_type == "SHT_NULL":
|
||||
return "unknown-section-index"
|
||||
if image_type == "ET_REL": return "section-offset"
|
||||
if raw.st_info.type == "STT_TLS": return "tls-offset"
|
||||
return "linked-virtual-address"
|
||||
|
||||
|
||||
def relocation_target(image_type, section_index, offset, sections):
|
||||
if image_type != "ET_REL":
|
||||
return {"kind": "linked-virtual-address", "address": address(offset)}
|
||||
if section_index == 0 or sections[section_index].header.sh_type == "SHT_NULL":
|
||||
return {"kind": "unknown-section", "reported_section_index": section_index,
|
||||
"offset": address(offset), "unknown_reason": "undefined-section-reference" if section_index == 0 else "inactive-section-header"}
|
||||
return {"kind": "section-offset", "section_index": section_index, "offset": address(offset)}
|
||||
|
||||
|
||||
def inspect_elf(path, cache):
|
||||
for package, expected in PACKAGES.items():
|
||||
try:
|
||||
version = importlib.metadata.version(package)
|
||||
except importlib.metadata.PackageNotFoundError as error:
|
||||
raise LayoutFailure("unavailable", "Selected Python lacks " + package + " " + expected + ".") from error
|
||||
if version != expected:
|
||||
raise LayoutFailure("unsupported", "Expected " + package + " " + expected + "; selected Python reports " + version + ".")
|
||||
content = path.read_bytes()
|
||||
if len(content) < 4 or content[:4] != b"\x7fELF":
|
||||
raise LayoutFailure("format", "Selected object does not contain an ELF header.")
|
||||
if len(content) < 16:
|
||||
raise LayoutFailure("format", "Selected ELF identification is truncated.")
|
||||
if content[4] != 2 or content[5] != 1:
|
||||
raise LayoutFailure("unsupported-target", "Initial layout profile requires ELF64 little-endian objects.")
|
||||
if len(content) < 64:
|
||||
raise LayoutFailure("format", "Selected ELF64 header is truncated.")
|
||||
if int.from_bytes(content[18:20], "little") != 62:
|
||||
raise LayoutFailure("unsupported-target", "Initial layout profile requires x86-64 ELF objects.")
|
||||
if int.from_bytes(content[16:18], "little") not in (1, 2, 3):
|
||||
raise LayoutFailure("unsupported-target", "Selected ELF type is outside the EXEC/DYN/REL layout profile; recorded core analysis is separate.")
|
||||
from pwnlib.context import context
|
||||
with context.local(cache_dir=str(cache), log_level="warning"):
|
||||
from elftools.common.exceptions import ELFError
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from pwnlib.elf import ELF
|
||||
try:
|
||||
headers = ELFFile(io.BytesIO(content))
|
||||
section_count = headers.num_sections()
|
||||
if headers.header.e_shstrndx == 0xffff and section_count == 0:
|
||||
raise LayoutFailure("format", "ELF extended section-name index requires a declared section-zero header.")
|
||||
raw_name_index = headers.header.e_shstrndx
|
||||
if 0xff00 <= raw_name_index < 0xffff:
|
||||
raise LayoutFailure("format", f"ELF section-name index {raw_name_index} is reserved and cannot directly reference a section.")
|
||||
section_name_index = headers.get_shstrndx()
|
||||
if raw_name_index == 0xffff and section_name_index < 0xff00:
|
||||
raise LayoutFailure("format", f"ELF SHN_XINDEX resolves to ordinary index {section_name_index}; the escape requires an index at least SHN_LORESERVE (0xff00).")
|
||||
if section_name_index != 0:
|
||||
if section_name_index >= section_count:
|
||||
raise LayoutFailure("format", f"ELF section-name table index {section_name_index} lies outside the declared section count {section_count}.")
|
||||
names_header = headers._get_section_header(section_name_index)
|
||||
if names_header.sh_type != "SHT_STRTAB":
|
||||
raise LayoutFailure("format", f"ELF section-name table {section_name_index} is not SHT_STRTAB.")
|
||||
location(names_header.sh_offset, names_header.sh_size, len(content))
|
||||
except ELFError as error:
|
||||
raise LayoutFailure("format", str(error)) from error
|
||||
class LayoutELF(ELF):
|
||||
def _get_section_name(self, header):
|
||||
# SHN_UNDEF declares no names; upstream would otherwise read
|
||||
# strings at the inactive section-zero offset.
|
||||
return "" if section_name_index == 0 else super()._get_section_name(header)
|
||||
|
||||
# pyelftools 0.33 calls these documented filters internally;
|
||||
# pwntools 4.15.0's cached overrides do not accept the argument.
|
||||
# Forward the filter on this adapter instance, without modifying
|
||||
# either upstream package or process-global methods.
|
||||
def iter_segments(self, type=None):
|
||||
return (segment for segment in super().iter_segments()
|
||||
if type is None or segment.header.p_type == type)
|
||||
|
||||
def iter_sections(self, type=None):
|
||||
return (section for section in super().iter_sections()
|
||||
if type is None or section.header.sh_type == type)
|
||||
try:
|
||||
image = LayoutELF(str(path), checksec=False)
|
||||
if image.arch != "amd64" or image.bits != 64 or image.endian != "little":
|
||||
raise LayoutFailure("unsupported-target", "Initial layout profile requires x86-64 ELF64 little-endian objects.")
|
||||
if image.header.e_type not in ("ET_EXEC", "ET_DYN", "ET_REL"):
|
||||
raise LayoutFailure("unsupported-target", "Selected ELF type is outside the EXEC/DYN/REL layout profile; recorded core analysis is separate.")
|
||||
length = len(content)
|
||||
all_sections = list(image.iter_sections())
|
||||
shstrings = image.get_section(section_name_index) if all_sections and section_name_index != 0 else None
|
||||
sections, segments, symbols, relocations, packed_relatives = [], [], [], [], []
|
||||
needed, interpreters = [], []
|
||||
for index, section in enumerate(all_sections):
|
||||
h = section.header
|
||||
backing = h.sh_type not in ("SHT_NOBITS", "SHT_NULL")
|
||||
if backing: location(h.sh_offset, h.sh_size, length)
|
||||
sections.append({
|
||||
"index": index, "name": name_reference(section.name, shstrings, h.sh_name, content) if shstrings is not None else {"display": "", "bytes_base64": None, "location": None, "unknown_reason": "ELF declares no section-name table (SHN_UNDEF index 0)."}, "name_offset": address(h.sh_name), "type": h.sh_type,
|
||||
"header_location": location(image.header.e_shoff + index * image.header.e_shentsize, image.header.e_shentsize, length),
|
||||
"address": address(h.sh_addr), "offset": address(h.sh_offset),
|
||||
"size": address(h.sh_size), "alignment": address(h.sh_addralign),
|
||||
"flags": address(h.sh_flags), "link": h.sh_link, "info": h.sh_info,
|
||||
"entry_size": address(h.sh_entsize), "file_backing": "file" if backing else "none",
|
||||
})
|
||||
if h.sh_type in ("SHT_SYMTAB", "SHT_DYNSYM"):
|
||||
if h.sh_entsize < image.structs.Elf_Sym.sizeof():
|
||||
raise LayoutFailure("format", "ELF symbol entry is smaller than the decoded Elf64_Sym structure.")
|
||||
if h.sh_link >= len(all_sections) or all_sections[h.sh_link].header.sh_type != "SHT_STRTAB":
|
||||
raise LayoutFailure("format", f"ELF symbol table {index} links to invalid string table {h.sh_link}.")
|
||||
strings = all_sections[h.sh_link]
|
||||
for entry, symbol in enumerate(section.iter_symbols()):
|
||||
raw = symbol.entry
|
||||
symbols.append({
|
||||
"table_index": index, "entry_index": entry,
|
||||
"name": name_reference(symbol.name, strings, raw.st_name, content), "name_offset": address(raw.st_name),
|
||||
"location": location(h.sh_offset + entry * h.sh_entsize, h.sh_entsize, length),
|
||||
"value": address(raw.st_value), "value_meaning": symbol_value_meaning(raw, image.header.e_type, all_sections),
|
||||
"size": address(raw.st_size), "binding": raw.st_info.bind, "type": raw.st_info.type,
|
||||
"visibility": raw.st_other.visibility, "section_index": raw.st_shndx,
|
||||
})
|
||||
if h.sh_type in ("SHT_REL", "SHT_RELA"):
|
||||
if image.header.e_type == "ET_REL" and h.sh_info >= len(all_sections):
|
||||
raise LayoutFailure("format", f"ELF relocation section {index} references missing target section {h.sh_info}.")
|
||||
symbol_table = None
|
||||
if h.sh_link != 0:
|
||||
if h.sh_link >= len(all_sections) or all_sections[h.sh_link].header.sh_type not in ("SHT_SYMTAB", "SHT_DYNSYM"):
|
||||
raise LayoutFailure("format", f"ELF relocation section {index} links to invalid symbol table {h.sh_link}.")
|
||||
symbol_table = all_sections[h.sh_link]
|
||||
for entry, relocation in enumerate(section.iter_relocations()):
|
||||
raw = relocation.entry
|
||||
if raw.r_info_sym != 0 and (symbol_table is None or raw.r_info_sym >= symbol_table.num_symbols()):
|
||||
raise LayoutFailure("format", f"ELF relocation section {index} entry {entry} references missing symbol {raw.r_info_sym} in table {h.sh_link}.")
|
||||
relocations.append({
|
||||
"section_index": index, "entry_index": entry, "reported_offset": address(raw.r_offset),
|
||||
"target": relocation_target(image.header.e_type, h.sh_info, raw.r_offset, all_sections),
|
||||
"location": location(h.sh_offset + entry * h.sh_entsize, h.sh_entsize, length),
|
||||
"type": raw.r_info_type, "symbol_table_index": h.sh_link, "symbol_index": raw.r_info_sym,
|
||||
"symbol_reference_meaning": "zero-symbol-value" if raw.r_info_sym == 0 else "symbol-table-entry",
|
||||
"addend": None if "r_addend" not in raw else str(raw.r_addend),
|
||||
})
|
||||
if h.sh_type == "SHT_RELR":
|
||||
packed_relatives.append({
|
||||
"section_index": index,
|
||||
"location": location(h.sh_offset, h.sh_size, length),
|
||||
"encoded_bytes_base64": base64.b64encode(content[h.sh_offset:h.sh_offset + h.sh_size]).decode("ascii"),
|
||||
"entries": [{"decoded_index": decoded, "reported_offset": address(relocation.entry.r_offset)} for decoded, relocation in enumerate(section.iter_relocations())],
|
||||
"offset_meaning": "unknown" if image.header.e_type == "ET_REL" else "linked-virtual-address",
|
||||
"evidence_kind": "derived", "entry_source_locations": None, "addends": None,
|
||||
})
|
||||
for index, segment in enumerate(image.iter_segments()):
|
||||
h = segment.header
|
||||
backing = h.p_type != "PT_NULL" and h.p_filesz != 0
|
||||
if backing: location(h.p_offset, h.p_filesz, length)
|
||||
if h.p_type == "PT_LOAD" and h.p_memsz < h.p_filesz:
|
||||
raise LayoutFailure("format", "ELF load segment memory size is smaller than its file size.")
|
||||
segments.append({
|
||||
"index": index, "type": h.p_type, "offset": address(h.p_offset),
|
||||
"header_location": location(image.header.e_phoff + index * image.header.e_phentsize, image.header.e_phentsize, length),
|
||||
"file_size": address(h.p_filesz), "memory_size": address(h.p_memsz),
|
||||
"virtual_address": address(h.p_vaddr), "physical_address": address(h.p_paddr),
|
||||
"alignment": address(h.p_align), "flags": address(h.p_flags),
|
||||
"file_backing": "file" if backing else "none",
|
||||
"permissions": None if h.p_type == "PT_NULL" else {"read": bool(h.p_flags & 4), "write": bool(h.p_flags & 2), "execute": bool(h.p_flags & 1)},
|
||||
})
|
||||
if h.p_type == "PT_DYNAMIC":
|
||||
tag_size = image.structs.Elf_Dyn.sizeof()
|
||||
tags = []
|
||||
for tag_index in range(h.p_filesz // tag_size):
|
||||
tag = segment._get_tag(tag_index)
|
||||
tags.append(tag)
|
||||
if tag.d_tag == "DT_NULL":
|
||||
break
|
||||
else:
|
||||
raise LayoutFailure("format", f"ELF dynamic segment {index} has no complete DT_NULL terminator within its reported file range.")
|
||||
for tag in tags:
|
||||
if tag.d_tag == "DT_NEEDED":
|
||||
needed.append(dynamic_name_reference(tags, tag.d_val, image, content))
|
||||
if h.p_type == "PT_INTERP":
|
||||
raw = content[h.p_offset:h.p_offset + h.p_filesz]
|
||||
end = raw.find(b"\0")
|
||||
if end < 0:
|
||||
raise LayoutFailure("format", "ELF interpreter lacks a reported terminator.")
|
||||
interpreters.append({"display": raw[:end].decode("utf-8", "replace"), "bytes_base64": base64.b64encode(raw[:end]).decode("ascii"), "location": location(h.p_offset, end + 1, length), "unknown_reason": None})
|
||||
return {
|
||||
"format": "elf", "architecture": {"machine": image.header.e_machine, "bits": image.bits, "byte_order": image.endian},
|
||||
"image_type": image.header.e_type,
|
||||
"entry_point": {"reported_value": address(image.header.e_entry), "meaning": "not-applicable" if image.header.e_type == "ET_REL" else "absent" if image.header.e_entry == 0 else "linked-virtual-address", "execution_status": "unknown"},
|
||||
"runtime_load_base": None, "sections": sections, "segments": segments, "symbols": symbols, "relocations": relocations,
|
||||
"packed_relative_relocations": packed_relatives, "relocation_inventory_completeness": "unknown",
|
||||
"linkage": {"needed_libraries": needed, "interpreters": interpreters,
|
||||
"got": [{"display_name": name, "address": address(value)} for name, value in image.got.items()],
|
||||
"plt": [{"display_name": name, "address": address(value)} for name, value in image.plt.items()],
|
||||
"convenience_maps_completeness": "unknown", "runtime_library_paths": None},
|
||||
"mitigations": {"evidence_kind": "inferred", "position_independent": image.pie, "nx_indicator": image.nx, "executable_stack_indicator": image.execstack, "stack_canary_indicator": image.canary, "relro": image.relro},
|
||||
"limitations": [
|
||||
"PT_NULL payload fields are unused and retain their reported numbers without file-byte or permission claims. Zero-file-size segments provide no file bytes.",
|
||||
"SHN_XINDEX symbol values remain unresolved in this upstream representation; an external index table does not establish a resolved index in this report.",
|
||||
"Reported values preserve linked addresses, section offsets, TLS offsets, alignment and absolute values separately. Runtime load base and library paths are unknown.",
|
||||
"Names are display strings plus raw bytes and file ranges where resolvable; section/table/entry indices preserve identity and duplicates. Name ranges include their terminating NUL; raw name bytes exclude it.",
|
||||
"Ordinary relocation rows cover REL/RELA. RELR tables retain complete encoded bytes and upstream-decoded offsets as derived evidence; per-offset packed-word locations and implicit addends remain unknown. Relocation inventory completeness is unknown, including other encodings and missing section tables.",
|
||||
"Relocation symbol index zero uses a zero symbol value without a table lookup. Positive indices reference validated original symbol-table entries; raw table and symbol indices remain reported.",
|
||||
"Relocatable relocation targets with SHN_UNDEF or inactive SHT_NULL headers retain their reported section index/offset as unknown; they do not establish an associated target section.",
|
||||
"GOT/PLT are derived upstream convenience maps; PLT inference can emulate selected instructions in Unicorn. They do not establish target runtime behavior; aliases may collapse and warnings may indicate incomplete coverage. Their completeness is unknown.",
|
||||
"Mitigations are upstream static heuristics, not runtime protection. ET_DYN does not prove an executable; absent canary symbols do not prove every function unprotected.",
|
||||
"This profile inspects ELF EXEC/DYN/REL layout only. It does not analyze recorded cores, resolve loaded libraries, launch the target as a host process or start a debugger.",
|
||||
"Symbol/relocation inventories reflect original section tables. A sectionless image can still report dynamic dependency names; missing tables do not prove absence of dynamic symbols or relocations.",
|
||||
*(["Absent section-name table: section-dependent RELRO/canary heuristics may be incomplete; protection coverage is unknown."] if all_sections and section_name_index == 0 else []),
|
||||
*(["Sectionless images lack the .dynamic section and symbol tables used by upstream heuristics. RELRO/canary indicators may be incomplete; their values remain reported static candidates, with protection coverage unknown."] if not all_sections else []),
|
||||
],
|
||||
}
|
||||
except LayoutFailure:
|
||||
raise
|
||||
except (ELFError, ValueError, AssertionError, IndexError) as error:
|
||||
raise LayoutFailure("format", "Selected ELF failed unchanged upstream structural parsing: " + str(error)) from error
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
runtime_path = Path(__file__).with_name("decoder_runtime.py")
|
||||
runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
|
||||
exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
|
||||
runtime["main"](sys.argv[-1], inspect_elf, LayoutFailure, PROFILE)
|
||||
@@ -1,39 +0,0 @@
|
||||
"""Catch typed resource failures while loading or running the owned recorded-core adapter.
|
||||
|
||||
Only this package's fixed sibling is executed; caller input stays in request.json.
|
||||
Interpreter startup failures before this boundary remain unclassified.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def resource_exit(status, marker):
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
os.write(descriptor, marker)
|
||||
os.close(descriptor)
|
||||
except (OSError, MemoryError):
|
||||
# Without a complete marker the parent keeps this failure unclassified.
|
||||
pass
|
||||
os._exit(status)
|
||||
|
||||
try:
|
||||
implementation = os.path.join(os.path.dirname(__file__), "recorded_crash_impl.py")
|
||||
with open(implementation, "rb") as handle:
|
||||
source = handle.read()
|
||||
exec(compile(source, implementation, "exec"), {
|
||||
"__file__": implementation,
|
||||
"__name__": "__main__",
|
||||
})
|
||||
except MemoryError:
|
||||
# Reserved status: an observed allocation failure, including imports/compile.
|
||||
resource_exit(75, b"M")
|
||||
except OSError as error:
|
||||
# Linux ENOMEM is an observed allocation failure, independent of error text.
|
||||
if error.errno == 12:
|
||||
resource_exit(75, b"M")
|
||||
# Linux EFBIG is observed directly, including a partial limits/reply write.
|
||||
if error.errno == 27:
|
||||
resource_exit(76, b"F")
|
||||
raise
|
||||
@@ -1,143 +0,0 @@
|
||||
"""Bounded Linux core evidence via unchanged ELFFile/structs and pwntools ctypes.
|
||||
|
||||
No Corefile-selected thread, executable guessing, process lookup or target launch.
|
||||
"""
|
||||
import base64
|
||||
import ctypes
|
||||
import importlib.metadata
|
||||
import io
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
PROFILE = "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2"
|
||||
|
||||
|
||||
class CoreFailure(Exception):
|
||||
def __init__(self, reason, message):
|
||||
super().__init__(message)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def encoded(value):
|
||||
return base64.b64encode(value).decode("ascii")
|
||||
|
||||
|
||||
def location(offset, size, limit):
|
||||
if not 0 <= offset <= limit or not 0 <= size <= limit - offset:
|
||||
raise CoreFailure("format", "Recorded source range exceeds its declared container.")
|
||||
return {"offset": hex(offset), "bytes": hex(size)}
|
||||
|
||||
|
||||
def inspect_core(path, cache):
|
||||
for package, required in {"pwntools": "4.15.0", "pyelftools": "0.33", "unicorn": "2.1.2"}.items():
|
||||
try:
|
||||
actual = importlib.metadata.version(package)
|
||||
except importlib.metadata.PackageNotFoundError as error:
|
||||
raise CoreFailure("unavailable", "Selected Python lacks " + package + " " + required + ".") from error
|
||||
if actual != required:
|
||||
raise CoreFailure("unsupported", "Expected " + package + " " + required + "; selected Python reports " + actual + ".")
|
||||
content = path.read_bytes()
|
||||
if len(content) < 16 or content[:4] != b"\x7fELF":
|
||||
raise CoreFailure("format", "Selected recording lacks a complete ELF identification.")
|
||||
if content[4:6] != b"\x02\x01":
|
||||
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires ELF64 little-endian.")
|
||||
if len(content) < 64:
|
||||
raise CoreFailure("format", "Selected ELF64 core header is truncated.")
|
||||
# Linux commonly emits ELFOSABI_NONE (0), also accepted as GNU/Linux (3).
|
||||
# Other declared OSABIs must never select Linux note ctypes.
|
||||
if content[7] not in (0, 3) or content[8] != 0:
|
||||
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires System V or GNU/Linux OSABI with ABI version zero; selected OSABI=" + str(content[7]) + ", ABI version=" + str(content[8]) + ".")
|
||||
if int.from_bytes(content[16:18], "little") != 4 or int.from_bytes(content[18:20], "little") != 62:
|
||||
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires x86-64 ET_CORE.")
|
||||
from elftools.common.exceptions import ELFError
|
||||
from elftools.common.utils import struct_parse
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from pwnlib.elf.datatypes import elf_prstatus_amd64, elf_siginfo_64, user_regs_struct_amd64
|
||||
try:
|
||||
image = ELFFile(io.BytesIO(content))
|
||||
header = image.header
|
||||
count = image.num_segments()
|
||||
if count and header.e_phentsize < image.structs.Elf_Phdr.sizeof():
|
||||
raise CoreFailure("format", "Program-header entry is shorter than the required ELF64 structure.")
|
||||
location(header.e_phoff, count * header.e_phentsize, len(content))
|
||||
segments, notes, padding, threads, signals = [], [], [], [], []
|
||||
for segment_index, segment in enumerate(image.iter_segments()):
|
||||
h = segment.header
|
||||
backing = h.p_type != "PT_NULL" and h.p_filesz > 0
|
||||
if backing:
|
||||
location(h.p_offset, h.p_filesz, len(content))
|
||||
if h.p_type == "PT_LOAD" and h.p_filesz > h.p_memsz:
|
||||
raise CoreFailure("format", "Recorded PT_LOAD file bytes exceed memory size.")
|
||||
segments.append({"index": segment_index, "type": h.p_type,
|
||||
"header_location": location(header.e_phoff + segment_index * header.e_phentsize, header.e_phentsize, len(content)),
|
||||
"offset": hex(h.p_offset), "file_size": hex(h.p_filesz), "memory_size": hex(h.p_memsz),
|
||||
"virtual_address": hex(h.p_vaddr), "physical_address": hex(h.p_paddr), "alignment": hex(h.p_align), "flags": hex(h.p_flags), "file_backing": "file" if backing else "none"})
|
||||
if h.p_type != "PT_NOTE" or h.p_filesz == 0:
|
||||
continue
|
||||
position, end = h.p_offset, h.p_offset + h.p_filesz
|
||||
header_size = image.structs.Elf_Nhdr.sizeof()
|
||||
while position < end:
|
||||
if end - position < header_size:
|
||||
raw = content[position:end]
|
||||
if any(raw):
|
||||
raise CoreFailure("format", "Nonzero trailing note bytes lack a complete note header.")
|
||||
padding.append({"segment_index": segment_index, "location": location(position, len(raw), end), "bytes_base64": encoded(raw)})
|
||||
break
|
||||
note = struct_parse(image.structs.Elf_Nhdr, image.stream, stream_pos=position)
|
||||
owner_start = position + header_size
|
||||
descriptor_start = owner_start + ((note.n_namesz + 3) // 4) * 4
|
||||
note_end = descriptor_start + ((note.n_descsz + 3) // 4) * 4
|
||||
location(position, note_end - position, end)
|
||||
owner = content[owner_start:owner_start + note.n_namesz]
|
||||
if owner and b"\0" not in owner:
|
||||
raise CoreFailure("format", "Declared note owner has no NUL terminator.")
|
||||
display = owner.split(b"\0", 1)[0].decode("utf-8", "replace") if owner else None
|
||||
descriptor = content[descriptor_start:descriptor_start + note.n_descsz]
|
||||
note_index = len(notes)
|
||||
notes.append({"index": note_index, "segment_index": segment_index, "type": note.n_type,
|
||||
"location": location(position, note_end - position, end),
|
||||
"owner_location": location(owner_start, len(owner), end), "owner_bytes_base64": encoded(owner), "owner_display": display,
|
||||
"descriptor_location": location(descriptor_start, len(descriptor), end), "descriptor_bytes_base64": encoded(descriptor)})
|
||||
position = note_end
|
||||
# Only the initial Linux CORE ABI is interpreted. Other owners retain exact bytes.
|
||||
if owner != b"CORE\0":
|
||||
continue
|
||||
if note.n_type == "NT_PRSTATUS":
|
||||
if len(descriptor) < ctypes.sizeof(elf_prstatus_amd64):
|
||||
raise CoreFailure("format", "Linux amd64 NT_PRSTATUS descriptor is truncated.")
|
||||
status = elf_prstatus_amd64.from_buffer_copy(descriptor)
|
||||
registers = []
|
||||
for name, kind in user_regs_struct_amd64._fields_:
|
||||
relative = elf_prstatus_amd64.pr_reg.offset + getattr(user_regs_struct_amd64, name).offset
|
||||
registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)})
|
||||
threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers})
|
||||
elif note.n_type == "NT_SIGINFO":
|
||||
if len(descriptor) < ctypes.sizeof(elf_siginfo_64):
|
||||
raise CoreFailure("format", "Linux amd64 NT_SIGINFO descriptor is truncated.")
|
||||
signal = elf_siginfo_64.from_buffer_copy(descriptor)
|
||||
number = ctypes.c_int32(signal.si_signo).value
|
||||
code = ctypes.c_int32(signal.si_code).value
|
||||
known = number == 11 and code in (1, 2)
|
||||
signals.append({"note_index": note_index, "number": number, "code": code, "errno": ctypes.c_int32(signal.si_errno).value,
|
||||
"fault_address": hex(signal.sigfault_addr) if known else None,
|
||||
"fault_address_meaning": "recorded-sigsegv-address" if known else "unknown", "thread_association": "unknown"})
|
||||
return {"format": "elf-core", "architecture": "x86_64-little-endian", "target_execution": "not-performed", "live_process_identity": "unknown",
|
||||
"segments": segments, "notes": notes, "note_padding": padding, "threads": threads, "signals": signals,
|
||||
"note_interpretation_completeness": "unknown",
|
||||
"limitations": ["The initial Linux note profile accepts System V/unspecified or GNU/Linux OSABI with ABI version zero; an unspecified OSABI alone does not prove operating-system origin.",
|
||||
"Historical PID and signal values are recorded metadata, not current process identity or attach authority.",
|
||||
"Only exact Linux CORE NT_PRSTATUS/NT_SIGINFO owners are interpreted; every descriptor and owner retains original bytes. Other note semantics remain unknown.",
|
||||
"NT_SIGINFO thread association is unknown. Only SIGSEGV SEGV_MAPERR/SEGV_ACCERR initially establish a fault-address union meaning.",
|
||||
"PT_LOAD file bytes are recorded bytes. Undumped memory and current executable/library identity remain unknown; no current host file is resolved from a recorded pathname.",
|
||||
"Missing notes/registers/signals do not establish their absence at the original crash. Full unwinding, executable pairing and live control are not provided."]}
|
||||
except CoreFailure:
|
||||
raise
|
||||
except (ELFError, ValueError, AssertionError, IndexError) as error:
|
||||
raise CoreFailure("format", "Recorded core failed unchanged upstream structural parsing: " + str(error)) from error
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
runtime_path = Path(__file__).with_name("decoder_runtime.py")
|
||||
runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
|
||||
exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
|
||||
runtime["main"](sys.argv[-1], inspect_core, CoreFailure, PROFILE)
|
||||
@@ -1,85 +0,0 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
|
||||
export default defineConfig({
|
||||
title: "REA",
|
||||
description:
|
||||
"Reverse engineer binaries, applications, and runtime behavior with one CLI and MCP server.",
|
||||
lang: "en-US",
|
||||
base: "/rea/",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "Get started", link: "/installation" },
|
||||
{ text: "Reference", link: "/mcp-contracts" },
|
||||
{ text: "Roadmap", link: "/roadmap" },
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
text: "Getting started",
|
||||
items: [
|
||||
{ text: "Installation and setup", link: "/installation" },
|
||||
{ text: "CLI and Evidence", link: "/cli" },
|
||||
{ text: "Agent prompts", link: "/mcp-prompts" },
|
||||
{ text: "Roadmap", link: "/roadmap" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Investigation guides",
|
||||
items: [
|
||||
{ text: "Native binaries", link: "/native-investigation" },
|
||||
{
|
||||
text: "JavaScript and Electron",
|
||||
link: "/javascript-artifact-reconstruction",
|
||||
},
|
||||
{
|
||||
text: "JavaScript workflows",
|
||||
link: "/javascript-application-workflows",
|
||||
},
|
||||
{ text: "Browser observation", link: "/browser-observation" },
|
||||
{ text: "Electron observation", link: "/electron-observation" },
|
||||
{ text: "Process capture", link: "/process-capture" },
|
||||
{ text: ".NET assemblies", link: "/managed-code-analysis" },
|
||||
{ text: "Android applications", link: "/android-analysis" },
|
||||
{ text: "Apple applications", link: "/apple-application-analysis" },
|
||||
{ text: "Firmware", link: "/firmware-analysis" },
|
||||
{
|
||||
text: "Reconstruction readiness",
|
||||
link: "/reconstruction-readiness",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Provider guides",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "IDA", link: "/ida-provider" },
|
||||
{ text: "Ghidra on Windows", link: "/windows-ghidra-p0" },
|
||||
{ text: "Ghidra DOS analysis", link: "/ghidra-dos" },
|
||||
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
|
||||
{ text: "Provider evaluation", link: "/provider-evaluation" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Reference and development",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "MCP contracts", link: "/mcp-contracts" },
|
||||
{ text: "Tool design", link: "/tool-design" },
|
||||
{ text: "Testing", link: "/testing" },
|
||||
{ text: "Releasing", link: "/releasing" },
|
||||
{ text: "Architecture decisions", link: "/adr/README" },
|
||||
],
|
||||
},
|
||||
],
|
||||
search: { provider: "local" },
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/morluto/rea" },
|
||||
{ icon: "discord", link: "https://discord.gg/GkcryMnJDM" },
|
||||
],
|
||||
editLink: {
|
||||
pattern: "https://github.com/morluto/rea/edit/main/docs/:path",
|
||||
},
|
||||
footer: {
|
||||
message: "Released under the MIT License.",
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,578 +0,0 @@
|
||||
# ADR-0001: Provider selection and analysis profiles
|
||||
|
||||
- Status: Accepted
|
||||
- Date: 2026-07-15
|
||||
- Implementation status: The provider registry, deterministic selection,
|
||||
target binding, analysis-profile commitment, and snapshot/Evidence migration
|
||||
are implemented. Ghidra discovery, target/profile resolution, doctor checks,
|
||||
the private headless-session foundation, thirteen read-only inventory/load-image
|
||||
capabilities, and twelve function-analysis capabilities are implemented with
|
||||
real cross-format conformance. Linux/macOS also supports atomic session
|
||||
function annotations; experimental Windows x64 P0 uses bundled native controls
|
||||
and admits only the 25 read-only operations. See the
|
||||
[release boundary](../installation.md#released-package-and-main).
|
||||
|
||||
## Context
|
||||
|
||||
Before this decision, REA composed the artifact, native macOS, and Hopper
|
||||
providers with `CompositeProvider`. That class intentionally requires every
|
||||
operation to have one route. Hopper and Ghidra will both implement the deep
|
||||
static-analysis operation family, so putting both into that composite would
|
||||
either fail at construction or make array order an undeclared selection policy.
|
||||
|
||||
The active `BinarySession` also owned one composite client. `open_binary` had no
|
||||
provider selector, and the domain `BinaryTarget` contained Hopper loader
|
||||
arguments. Analysis snapshot v1 committed those loader arguments and each
|
||||
cached entry's provider identity, but it did not commit the selected analysis
|
||||
engine, engine version, or complete analysis configuration. Two analyses of the
|
||||
same bytes could therefore disagree because of language, compiler, loader,
|
||||
analyzer, or provider-version differences while appearing target-compatible.
|
||||
|
||||
The decision addressed the earlier representations of
|
||||
[`CompositeProvider`](https://github.com/morluto/rea/blob/main/src/application/binary/CompositeProvider.ts),
|
||||
[runtime composition](https://github.com/morluto/rea/blob/main/src/composition/binary.ts),
|
||||
[`BinarySession`](https://github.com/morluto/rea/blob/main/src/application/binary/BinarySession.ts),
|
||||
[`BinaryTarget`](https://github.com/morluto/rea/blob/main/src/domain/binaryTarget.ts),
|
||||
[analysis snapshot](https://github.com/morluto/rea/blob/main/src/domain/analysisSnapshot.ts), and
|
||||
[session lifecycle inputs](https://github.com/morluto/rea/blob/main/src/contracts/sessionLifecycleInputs.ts).
|
||||
|
||||
The next deep provider must not weaken the existing product invariants:
|
||||
|
||||
- stable provider-neutral CLI and MCP tool names;
|
||||
- exact provenance and explicit unavailable or degraded capabilities;
|
||||
- CLI and MCP parity;
|
||||
- no silent provider fallback;
|
||||
- provider-specific code outside the domain and application models;
|
||||
- local, actionable diagnostics with only genuine secrets redacted; and
|
||||
- snapshot reuse only when semantic analysis inputs are identical.
|
||||
|
||||
## Decision drivers
|
||||
|
||||
1. Hopper and Ghidra need to declare overlapping operations without ambiguous
|
||||
dispatch.
|
||||
2. A target must have one explainable deep-analysis producer for its lifetime.
|
||||
3. Target-free discovery must remain useful and must not start an analysis
|
||||
process.
|
||||
4. Existing Hopper-only configurations must keep working when no selector is
|
||||
supplied.
|
||||
5. Adding a second installed provider must not silently change results.
|
||||
6. Snapshot and Evidence compatibility must include every setting that can
|
||||
materially change analysis.
|
||||
7. Provider-specific open settings must not leak into `BinaryTarget` or become
|
||||
generic application fields.
|
||||
|
||||
## Terminology
|
||||
|
||||
- **Deep provider**: an engine such as Hopper or Ghidra that implements the
|
||||
overlapping static-analysis operation family.
|
||||
- **Auxiliary provider**: a provider with a disjoint operation family, such as
|
||||
artifact inventory or native macOS inspection.
|
||||
- **Candidate**: a registered deep provider before target selection.
|
||||
- **Binding**: the selected deep provider and analysis profile attached to one
|
||||
active target.
|
||||
- **Analysis profile**: a canonical commitment to the selected provider,
|
||||
provider version, and normalized semantic analysis settings.
|
||||
- **Available**: the adapter and its required local runtime can be used on this
|
||||
host.
|
||||
- **Supported**: the candidate accepts the parsed target and supplied open
|
||||
options. Availability and target support are separate observations.
|
||||
|
||||
## Decision
|
||||
|
||||
### 1. Separate disjoint composition from overlapping selection
|
||||
|
||||
`CompositeProvider` remains the mechanism for operation families whose routes
|
||||
are disjoint. It must continue rejecting duplicate operation declarations. It
|
||||
must not acquire priority, racing, fallback, or provider-selection behavior.
|
||||
|
||||
A new `AnalysisProviderRegistry` owns the configured deep-provider candidates.
|
||||
Provider IDs are unique; duplicate IDs are a configuration error. The registry
|
||||
may hold multiple candidates that declare the same operation. It performs
|
||||
candidate discovery, target-support evaluation, option parsing, profile
|
||||
resolution, and selection, but it does not choose a different provider for each
|
||||
request.
|
||||
|
||||
After selection, the session composes exactly one bound deep provider with the
|
||||
auxiliary disjoint families:
|
||||
|
||||
```text
|
||||
target-free discovery
|
||||
|
|
||||
AnalysisProviderRegistry
|
||||
/ \
|
||||
Hopper Ghidra
|
||||
\ /
|
||||
one selected binding
|
||||
|
|
||||
+-------------------------+-------------------------+
|
||||
| | |
|
||||
selected deep family artifact family native family
|
||||
+-------------------------+-------------------------+
|
||||
|
|
||||
BinarySession
|
||||
```
|
||||
|
||||
An auxiliary operation continues to report its actual auxiliary provider as
|
||||
provenance. A deep operation always reports the provider in the active binding.
|
||||
The synthetic composite identity is never evidence that two engines jointly
|
||||
produced one observation.
|
||||
|
||||
Operation ownership is fixed by family:
|
||||
|
||||
| Operation family | Executor |
|
||||
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| Deep direct operations and provider-native function analysis | The one bound Hopper or Ghidra provider |
|
||||
| Application-enhanced deep workflows | Application composition over the bound provider's declared operations |
|
||||
| Native macOS inspection | The disjoint native provider |
|
||||
| Artifact inventory and extraction | The disjoint artifact provider |
|
||||
| Browser and Electron observation | Their independently authorized CDP providers; they do not participate in deep-provider selection |
|
||||
| Session lifecycle, Evidence, comparisons, and reconstruction | The application/session layer using the exact provider provenance recorded by their source operations |
|
||||
|
||||
Selecting a deep provider selects the whole deep family. If that provider marks
|
||||
an operation unavailable, REA returns the unavailability; it does not borrow
|
||||
that one operation from another deep provider. Application-enhanced workflows
|
||||
run only when the bound provider supplies their declared dependencies.
|
||||
|
||||
### 2. Discover candidates without launching analysis
|
||||
|
||||
Each registered candidate exposes three bounded adapter-owned observations:
|
||||
|
||||
1. identity and declared capability descriptors;
|
||||
2. host availability, including an actionable rejection when unavailable; and
|
||||
3. target support plus normalized profile resolution when a target is known.
|
||||
|
||||
Target-free discovery may inspect configuration, manifests, executable
|
||||
metadata, and documented version files. It must not import a target, create a
|
||||
project, open a GUI, or start the long-lived analysis process. Any short-lived
|
||||
version probe added later must be explicitly bounded and truthfully declared as
|
||||
a process effect.
|
||||
|
||||
Candidate output is sorted by provider ID for determinism. Sort order is not
|
||||
selection priority. Availability and support use stable codes plus a human
|
||||
diagnostic:
|
||||
|
||||
| Code | Meaning |
|
||||
| ---------------------------- | --------------------------------------------------------------- |
|
||||
| `not_configured` | Required provider location or configuration is absent |
|
||||
| `executable_missing` | The configured provider entry point is missing or not runnable |
|
||||
| `runtime_missing` | A required provider runtime, such as Java, is unavailable |
|
||||
| `unsupported_host` | The host platform or architecture is unsupported |
|
||||
| `unsupported_version` | The discovered provider or runtime version is unsupported |
|
||||
| `target_kind_unsupported` | The provider does not accept this target kind |
|
||||
| `target_format_unsupported` | The provider does not accept this target format |
|
||||
| `architecture_unsupported` | The selected target architecture is unsupported |
|
||||
| `target_role_unsupported` | The provider rejects this executable image role |
|
||||
| `managed_target_unsupported` | The provider does not admit managed code through this deep path |
|
||||
| `open_options_invalid` | The selected adapter rejected its open options |
|
||||
| `version_unresolved` | A concrete analysis-engine version could not be committed |
|
||||
|
||||
Diagnostics retain useful local paths, observed versions, architectures, and
|
||||
mismatch details. Credentials, authorization headers, license secrets, and
|
||||
other genuine secrets remain redacted.
|
||||
|
||||
### 3. Use one deterministic selection algorithm
|
||||
|
||||
The caller-visible selectors are:
|
||||
|
||||
- MCP `open_binary.provider_id`;
|
||||
- `rea ... --provider <provider-id|auto>` on every one-shot command that needs
|
||||
deep analysis; and
|
||||
- `REA_ANALYSIS_PROVIDER=<provider-id|auto>`, defaulting to `auto`.
|
||||
|
||||
The literal value `auto` is reserved and cannot be a provider ID. Precedence is
|
||||
request or CLI flag, then environment configuration, then the default `auto`.
|
||||
An explicit request for `auto` overrides a concrete environment preference and
|
||||
re-enters automatic selection.
|
||||
|
||||
Selection occurs after provider-neutral target parsing and before a deep client
|
||||
is created:
|
||||
|
||||
1. A concrete requested provider ID must exist, be available, support the
|
||||
target, accept its options, and resolve a concrete version and profile. Any
|
||||
failure returns a typed selection error; another provider is never tried.
|
||||
2. `auto` filters candidates by availability and target support.
|
||||
3. Exactly one usable candidate is selected with source
|
||||
`auto-single-candidate`.
|
||||
4. More than one usable candidate is an `ambiguous` selection error. The
|
||||
diagnostic lists the candidates and asks for `provider_id`, `--provider`, or
|
||||
`REA_ANALYSIS_PROVIDER`. REA does not prefer Hopper, Ghidra, registration
|
||||
order, the fastest startup, or the first health response.
|
||||
5. When no deep candidate is usable, an automatic open may still establish an
|
||||
unbound target so disjoint artifact or native operations remain available.
|
||||
Deep operations are then explicitly unavailable with the candidate
|
||||
rejections. A concrete request, or a concrete environment preference for a
|
||||
deep-eligible target, remains a hard requirement and fails instead of
|
||||
becoming unbound.
|
||||
6. A target that no deep adapter declares support for, such as an artifact-only
|
||||
target, is `not_applicable` rather than a failed deep analysis. Supplying a
|
||||
concrete provider in that open request is still an actionable unsupported-
|
||||
target error. A global environment preference does not make an artifact-only
|
||||
target require a deep provider.
|
||||
|
||||
This preserves target opening for non-Hopper capabilities on hosts without a
|
||||
deep engine while ensuring that installing a second usable deep engine never
|
||||
changes analysis silently.
|
||||
|
||||
### 4. Bind once and never fail over mid-session
|
||||
|
||||
An active deep binding contains:
|
||||
|
||||
```text
|
||||
canonical target identity
|
||||
selected provider identity and concrete version
|
||||
selection source: request | environment | auto-single-candidate
|
||||
analysis profile commitment
|
||||
owned provider client, once started
|
||||
```
|
||||
|
||||
The binding is immutable until `close_binary` or an explicit open/switch
|
||||
transaction. Provider client creation may remain lazy, but laziness cannot
|
||||
change which provider was selected.
|
||||
|
||||
- Opening the same canonical target without a new selector keeps the existing
|
||||
binding.
|
||||
- Opening the same target with the same provider and profile is idempotent.
|
||||
- A different explicit provider or a profile-changing option is a real switch:
|
||||
drain active calls, close owned resources, resolve the new binding, and start
|
||||
it as one serialized transaction.
|
||||
- If a switch fails, restore the prior target and binding on a best-effort
|
||||
transactional path and return the original switch error.
|
||||
- Provider process exit, timeout, cancellation, capability failure, or adapter
|
||||
error never triggers fallback. The binding stays visible and unavailable
|
||||
until the caller explicitly switches or closes it.
|
||||
- A snapshot never chooses or changes the provider. Selection happens first;
|
||||
snapshot compatibility is checked against the resulting binding.
|
||||
|
||||
Calls already admitted to one binding complete or cancel against that binding.
|
||||
No request can cross a concurrent target or provider transition.
|
||||
|
||||
### 5. Keep provider open options adapter-owned
|
||||
|
||||
`BinaryTarget` retains only provider-neutral source identity and classification:
|
||||
canonical path, source digest, generic kind and format, selected architecture,
|
||||
and available architectures. Hopper loader arguments leave the domain model.
|
||||
Provider-owned databases use a generic analysis-database classification; their
|
||||
adapter determines support. A legacy `.hop` target is therefore accepted only
|
||||
by Hopper without teaching the domain how to open it. Ghidra v1 continues to use
|
||||
private temporary projects and does not open user projects.
|
||||
|
||||
The generic transport may carry one bounded JSON `provider_options` object.
|
||||
Only the selected adapter parses its keys and reports `open_options_invalid` at
|
||||
that boundary. If this object is exposed over MCP, the CLI parity surface is
|
||||
`--provider-options-json` with the same JSON schema and limits. Unknown keys,
|
||||
wrong-provider options, excessive depth or size, and non-JSON values fail
|
||||
closed.
|
||||
|
||||
Environment settings that are inherently adapter-specific, such as the Hopper
|
||||
launcher or future Ghidra installation path, remain in adapter configuration.
|
||||
They do not become `BinaryTarget` fields. The application layer transports an
|
||||
opaque, already-bounded JSON value and the generic commitment described below;
|
||||
it does not interpret Hopper or Ghidra option names.
|
||||
|
||||
### 6. Commit a canonical analysis profile
|
||||
|
||||
Before creating a deep client, the selected adapter normalizes every semantic
|
||||
input that can affect analysis. Examples include Hopper loader selection and
|
||||
architecture flags, or Ghidra language ID, compiler specification, import mode,
|
||||
analyzer set, and analyzer options. Defaults are made explicit, unordered sets
|
||||
are sorted, and implicit host-dependent choices are resolved.
|
||||
|
||||
The adapter returns this provider-neutral envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": { "id": "ghidra", "version": "<concrete version>" },
|
||||
"parameters": {},
|
||||
"digest": "<sha256 of RFC 8785 canonical JSON excluding digest>"
|
||||
}
|
||||
```
|
||||
|
||||
`parameters` is bounded JSON interpreted only by that provider adapter. It
|
||||
contains analysis semantics, not credentials, authorization data, temporary
|
||||
paths, installation paths, process IDs, timestamps, or other incidental host
|
||||
state. The generic domain validates the envelope and digest but treats the
|
||||
parameters as opaque data.
|
||||
|
||||
A selected deep binding requires a concrete provider version. If the adapter
|
||||
cannot determine it, the candidate is unavailable with `version_unresolved`;
|
||||
REA does not persist a cache that merely commits `null` or "whatever is
|
||||
installed". Provider identity, normalized parameters, or provider version
|
||||
changes produce a different digest.
|
||||
|
||||
The same target, configuration, and provider must resolve the same profile over
|
||||
CLI and MCP. Profile resolution must be side-effect-free and complete before
|
||||
the provider imports the target.
|
||||
|
||||
### 7. Make snapshots provider- and profile-exact
|
||||
|
||||
Analysis snapshots replace Hopper loader arguments with an explicit binding
|
||||
commitment:
|
||||
|
||||
```text
|
||||
target: digest + generic format/kind + selected architecture
|
||||
binding: provider identity + analysis profile commitment
|
||||
entries: exact immutable queries
|
||||
evidence_bundle: retained Evidence records
|
||||
```
|
||||
|
||||
The snapshot target identity, canonical snapshot content digest, and every
|
||||
query ID commit the provider ID, concrete provider version, and profile digest.
|
||||
A cached entry is reusable only when all of the following match exactly:
|
||||
|
||||
- target digest, generic format and kind, and selected architecture;
|
||||
- provider ID and provider version;
|
||||
- profile digest;
|
||||
- operation and canonical parameters; and
|
||||
- the existing cacheability and immutability rules.
|
||||
|
||||
Provider or profile switches select a separate cache partition and cannot reuse
|
||||
entries from the previous binding. Auxiliary operations are cacheable only if
|
||||
their provider defines an equivalent explicit profile commitment; otherwise
|
||||
they remain Evidence but not snapshot entries.
|
||||
|
||||
Snapshot v1 cannot be parsed as the current snapshot format. Its `loader_args`
|
||||
cannot prove which engine version, defaults, or analyzer settings produced the
|
||||
entries, so snapshots must be recaptured before they can be used as a cache.
|
||||
An embedded Evidence bundle can still be imported separately through the
|
||||
Evidence import flow; it is never promoted into an analysis cache.
|
||||
|
||||
### 8. Add profile commitments to new deep Evidence while preserving legacy records
|
||||
|
||||
The optional `analysis_profile` field uses the canonical envelope above. Every
|
||||
newly created deep-analysis observation must include it, and its provider ID
|
||||
and version must match the record's provider. The field participates in the
|
||||
Evidence semantic ID.
|
||||
|
||||
Existing Evidence records without `analysis_profile` remain valid and keep
|
||||
their existing IDs. Absence on a legacy deep record means the analysis profile
|
||||
is unknown; REA does not infer or enrich it during import. Such evidence can be
|
||||
displayed and cited but cannot establish profile compatibility, seed snapshot
|
||||
replay, or support an "unchanged" conclusion that depends on identical analysis
|
||||
semantics.
|
||||
|
||||
Different providers or profile digests are never snapshot-compatible. A
|
||||
derived comparison may compare cross-provider normalized facts only when its
|
||||
own contract explicitly defines that semantic comparison. Exact pseudocode,
|
||||
provider text, or provider-specific metadata is not treated as equivalent. A
|
||||
missing or incompatible profile yields unknown or incompatible, never equal.
|
||||
|
||||
### 9. Expose selection truth in status and diagnostics
|
||||
|
||||
`binary_session`, `rea providers`, and `rea capabilities` expose the same
|
||||
candidate and binding model. The target-free form does not start providers and
|
||||
uses `unknown` for target support:
|
||||
|
||||
```json
|
||||
{
|
||||
"analysis_provider_binding": null,
|
||||
"analysis_provider_candidates": [
|
||||
{
|
||||
"provider": {
|
||||
"id": "hopper",
|
||||
"name": "Hopper Disassembler",
|
||||
"version": "<detected version>"
|
||||
},
|
||||
"availability": {
|
||||
"status": "available",
|
||||
"code": null,
|
||||
"reason": null,
|
||||
"diagnostics": {}
|
||||
},
|
||||
"target_support": {
|
||||
"status": "unknown",
|
||||
"code": null,
|
||||
"reason": "No target is open."
|
||||
},
|
||||
"selected": false,
|
||||
"capabilities": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
With a binding, `analysis_provider_binding` includes the selected identity,
|
||||
selection source, and complete profile commitment. Every candidate reports
|
||||
`supported`, `unsupported`, or `unknown` target support and whether it was
|
||||
selected. Candidate capability descriptors preserve supported, unsupported,
|
||||
and degraded truth independently.
|
||||
|
||||
An open target also exposes `analysis_run`. Its run ID is allocated by the
|
||||
session before client construction and propagated into selected Hopper or
|
||||
Ghidra bridge handshake and process environment. Dynamic clients retain
|
||||
provider-attributed lineage snapshots only after revalidating launcher
|
||||
parent/command and every observed launcher or descendant run token. `not_observed`
|
||||
means no dynamic provider has supplied a snapshot. Once one or more start,
|
||||
`snapshots` preserves every provider observation; each remains explicitly
|
||||
`unavailable` or `verified` and records its `observed_at` time. Retained
|
||||
snapshots are not live inventories, and a point-in-time observation never proves
|
||||
historical absence.
|
||||
|
||||
The existing flat `provider`, `providers`, and `capabilities` status fields stay
|
||||
during the 1.x compatibility window. Their effective deep-operation entries
|
||||
come only from the active binding; target-free ambiguity is unavailable rather
|
||||
than arbitrarily collapsed. The new candidate fields are authoritative for
|
||||
selection. A future major release may remove the synthetic composite provider
|
||||
field after callers have migrated.
|
||||
|
||||
Selection failures extend the tagged `ProviderSelectionError` with a stable
|
||||
reason:
|
||||
|
||||
- `unknown_provider`;
|
||||
- `provider_unavailable`;
|
||||
- `target_unsupported`;
|
||||
- `ambiguous`;
|
||||
- `invalid_options`.
|
||||
|
||||
The error includes the requested ID or `auto`, candidate IDs, rejection codes,
|
||||
and actionable local diagnostics. `AnalysisCapabilityUnavailableError` remains
|
||||
the error for an operation the selected provider cannot supply.
|
||||
`ProviderAdapterError` and more specific provider errors remain failures of the
|
||||
already selected provider. None authorizes fallback.
|
||||
|
||||
A snapshot whose provider or profile differs from the selected binding returns
|
||||
`EvidenceIntegrityError` with stable `profile_mismatch` details. It is an
|
||||
integrity incompatibility after selection, not permission to select the
|
||||
snapshot's provider.
|
||||
|
||||
### 10. Preserve CLI and MCP parity
|
||||
|
||||
- `open_binary.provider_id` and one-shot `--provider` use the same parser and
|
||||
selection service.
|
||||
- `provider_id: "auto"`, `--provider auto`, and
|
||||
`REA_ANALYSIS_PROVIDER=auto` have identical auto semantics.
|
||||
- Startup targets opened from environment configuration use the same service.
|
||||
- `rea providers`, `rea capabilities`, and target-free `binary_session` use the
|
||||
same candidate snapshot and reason codes.
|
||||
- Guided prompt `provider_id` context is a preference to forward into the open
|
||||
operation; it does not create a hidden routing channel.
|
||||
- Tool names and normalized output contracts remain provider-neutral. REA does
|
||||
not add `ghidra_decompile` or `hopper_decompile` tools.
|
||||
|
||||
## Compatibility and migration
|
||||
|
||||
| Existing behavior or data | Decision |
|
||||
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| No provider selector, only Hopper usable | `auto` selects the sole candidate; behavior remains equivalent |
|
||||
| No deep provider usable | Automatic open may remain unbound for auxiliary operations; deep capabilities explain why they are unavailable |
|
||||
| Hopper and Ghidra both usable, no preference | Open fails with `ambiguous`; installation order never becomes policy |
|
||||
| Existing Hopper environment settings | Remain supported as Hopper adapter configuration |
|
||||
| `open_binary` and CLI structured `loaderArgs` output | Retained as a deprecated Hopper-only compatibility projection for 1.x; the analysis profile is authoritative |
|
||||
| `BinaryTarget.loaderArgs` | Removed; Hopper derives them inside its adapter and commits normalized semantics in the profile |
|
||||
| Legacy `.hop` input | Classified generically as an analysis database and accepted only by Hopper |
|
||||
| Snapshot v1 | Not accepted as the current snapshot format; recapture before cache replay |
|
||||
| Existing Evidence without a profile | Accepted unchanged; profile compatibility is unknown |
|
||||
| New deep-analysis Evidence | Must include `analysis_profile`; its semantic ID commits the field |
|
||||
| Existing status fields | Retained for 1.x with additive binding and candidate fields |
|
||||
| Provider failure after selection | Returned from the selected provider; no transparent retry through another engine |
|
||||
|
||||
The deprecated `loaderArgs` response must never be read to select a provider or
|
||||
validate a snapshot. It is empty for non-Hopper bindings and may be removed only
|
||||
with a separately documented major contract change.
|
||||
|
||||
## Required behavior scenarios
|
||||
|
||||
| Scenario | Result |
|
||||
| ---------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| Hopper is the only usable candidate, selector omitted | Hopper is bound by `auto-single-candidate` |
|
||||
| Ghidra is the only usable candidate, selector omitted | Ghidra is bound by `auto-single-candidate` |
|
||||
| Both are usable, selector omitted | Typed `ambiguous` error; neither provider starts |
|
||||
| Environment selects Ghidra, request omits a selector | Ghidra is required and bound or the open fails with its rejection |
|
||||
| Environment selects Ghidra, request selects Hopper | Request wins; Hopper is required |
|
||||
| Environment selects Ghidra, request selects `auto` | Auto policy runs and is ambiguous when both are usable |
|
||||
| Unknown concrete provider ID | Typed `unknown_provider`; no target transition |
|
||||
| Selected provider rejects the target or options | Typed rejection from that candidate only |
|
||||
| Selected provider times out after binding | Binding remains selected and unavailable; no fallback |
|
||||
| Same target is reopened without a selector | Existing binding remains unchanged |
|
||||
| Same bytes are opened with a different profile | Real provider switch and a separate snapshot partition |
|
||||
| Snapshot provider or profile differs from the binding | Typed `EvidenceIntegrityError` with `profile_mismatch`; nothing is imported |
|
||||
| Artifact-only target has no deep candidate | Target opens unbound; disjoint artifact capabilities remain usable |
|
||||
| Environment prefers Ghidra for an artifact-only target | Deep selection is `not_applicable`; auxiliary operations remain usable |
|
||||
| Concrete deep provider is requested for artifact-only data | Typed `target_unsupported`; the request is not silently ignored |
|
||||
|
||||
## Consequences
|
||||
|
||||
Positive consequences:
|
||||
|
||||
- Hopper and Ghidra can coexist without ambiguous operation routes.
|
||||
- Provider choice is reproducible, visible, and stable for a target.
|
||||
- Installation of another provider cannot silently change analysis.
|
||||
- Snapshot and Evidence identities become analysis-configuration aware.
|
||||
- Provider adapters retain ownership of their option schemas and diagnostics.
|
||||
- Auxiliary capabilities remain usable when no deep engine is installed.
|
||||
|
||||
Costs and tradeoffs:
|
||||
|
||||
- Multi-provider hosts must choose when more than one candidate is usable.
|
||||
- Snapshots must be recaptured when their provider or profile binding changes.
|
||||
- Providers must expose deterministic availability, support, version, and
|
||||
profile resolution before they can be admitted.
|
||||
- Status and error contracts gain additive structured metadata.
|
||||
- Hopper needs an adapter-owned version/profile implementation before profile-
|
||||
exact snapshot persistence is complete.
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
### Let `CompositeProvider` choose the first duplicate route
|
||||
|
||||
Array order is not a product policy, becomes fragile during wiring changes, and
|
||||
hides ambiguity from callers.
|
||||
|
||||
### Race providers and keep the first healthy response
|
||||
|
||||
Machine load would determine provenance and results. It also starts resources
|
||||
the operator did not select and makes cancellation and cleanup harder.
|
||||
|
||||
### Fall back after a provider error
|
||||
|
||||
Mid-session fallback can combine incompatible engine state and evidence under
|
||||
one apparent target. Explicit close or switch is required instead.
|
||||
|
||||
### Add provider-prefixed tools
|
||||
|
||||
Parallel Hopper and Ghidra tool families would leak adapters into the product
|
||||
contract and prevent shared workflows from using capability metadata.
|
||||
|
||||
### Store raw provider options in `BinaryTarget`
|
||||
|
||||
That repeats the current Hopper coupling and makes every generic consumer learn
|
||||
provider schemas. Only canonical profile commitments cross the adapter boundary.
|
||||
|
||||
### Treat matching target bytes and provider ID as sufficient for snapshots
|
||||
|
||||
Provider versions, analyzer defaults, loaders, languages, and compiler models
|
||||
can change results without changing either value.
|
||||
|
||||
### Silently upgrade snapshot v1
|
||||
|
||||
The missing engine version and analysis settings cannot be reconstructed from
|
||||
Hopper loader arguments. A fabricated profile would turn unknown provenance
|
||||
into false certainty.
|
||||
|
||||
## Implementation progress
|
||||
|
||||
The first six implementation stages are shipped:
|
||||
|
||||
1. Provider-specific target state was replaced by generic profile and snapshot
|
||||
v2 commitments while preserving Hopper execution behavior.
|
||||
2. The registry, selectors, candidate status, binding lifecycle, and
|
||||
overlapping-provider production seams were added.
|
||||
3. Proven process-lifecycle primitives were extracted for shared use.
|
||||
4. Ghidra availability, target/profile resolution, doctor checks, and a private
|
||||
read-only headless session were admitted without declaring operations.
|
||||
5. Program identity, procedures, strings, symbols, memory blocks,
|
||||
address/name resolution, containing-procedure resolution, and search
|
||||
were admitted with exact wire schemas and real debug/stripped ELF
|
||||
conformance.
|
||||
6. Function metadata, persistent decompilation, assembly, resolved
|
||||
callers/callees, typed references, xrefs, CFG, and complete function dossiers
|
||||
were admitted. A serial per-Program queue keeps API access ordered without a
|
||||
fixed queue length or per-operation deadline. Real conformance covers
|
||||
x86-64 and AArch64 ELF, PE, Mach-O, stripped symbols, targetless indirect
|
||||
calls, cancellation, and cleanup.
|
||||
|
||||
Future stages may deepen format and indirect-flow coverage, but must continue
|
||||
to compare normalized semantics rather than provider-specific pseudocode or
|
||||
assembly text. Linux and macOS now admit atomic function-name and entry-comment
|
||||
edits in the ephemeral database, with readback, rollback, and cache invalidation.
|
||||
Executable bytes remain unchanged. Windows P0 retains its read-only boundary;
|
||||
GUI and other unimplemented mutations remain unavailable.
|
||||
@@ -1,551 +0,0 @@
|
||||
# ADR-0002: Controlled JavaScript replay authority and sandbox policy
|
||||
|
||||
- Status: Superseded; the controlled JavaScript replay tool was removed
|
||||
- Date: 2026-07-16
|
||||
- Implementation status: The implementation was removed. The design below is
|
||||
retained as historical context and does not describe a supported REA tool.
|
||||
|
||||
> Historical design note: The tool, sandbox backend, and plan/execute workflow
|
||||
> described here are no longer part of REA. For runtime behavior, run probes
|
||||
> directly against the application and use the available browser, Electron, or
|
||||
> process capture workflows.
|
||||
|
||||
## Context
|
||||
|
||||
REA can reconstruct JavaScript and Electron artifacts, observe an existing
|
||||
browser or Electron page through passive CDP, capture an explicitly approved
|
||||
process scenario, and correlate those observations without executing bundle
|
||||
code in the REA server. The next research workflow needs to execute selected,
|
||||
extracted JavaScript modules with controlled inputs and deterministic stubs so
|
||||
that parsers, sanitizers, serializers, and similar pure logic can be compared
|
||||
across versions.
|
||||
|
||||
That workflow crosses a qualitatively different boundary. Source recovered
|
||||
from an application is untrusted code. It can intentionally or accidentally
|
||||
read files, contact services, spawn processes, exhaust resources, corrupt the
|
||||
analysis process, forge protocol output, or interact with another same-user
|
||||
process. A JavaScript realm or Node.js `vm` context is useful for constructing
|
||||
an API surface, but it is not a security boundary. Node.js also documents its
|
||||
permission model as protection against accidental access by trusted code, not
|
||||
as containment for malicious code.
|
||||
|
||||
Existing authorities cannot be widened to cover this behavior:
|
||||
|
||||
- `browser_observe` and `electron_observe` authorize passive attachment to an
|
||||
already-running, operator-owned target. They do not authorize evaluation,
|
||||
navigation, input, or target lifecycle changes.
|
||||
- `process_capture` authorizes one declared host process scenario and explicitly
|
||||
states that it is not a sandbox. It cannot silently become permission to run
|
||||
recovered code.
|
||||
- static artifact reads authorize inspection of bytes, not execution of those
|
||||
bytes.
|
||||
- the Evidence authority `controlled-replay` already describes observations
|
||||
from an approved process experiment. Reusing that epistemic authority is
|
||||
correct, but reusing the process permission capability is not.
|
||||
|
||||
The implementation following this ADR must fail closed on hosts where REA
|
||||
cannot establish the promised operating-system boundary. It must not copy the
|
||||
research `vm.runInContext` scripts into the production server and call that
|
||||
isolation.
|
||||
|
||||
## Decision drivers
|
||||
|
||||
1. Passive observation must never imply authority to execute application code.
|
||||
2. The exact code, inputs, stubs, runtime, sandbox policy, and limits approved
|
||||
by the operator must be the ones executed.
|
||||
3. Recovered code must not run in the MCP server, CLI adapter, or any provider
|
||||
process.
|
||||
4. A sandbox failure must make the capability unavailable, not select a weaker
|
||||
execution mode.
|
||||
5. The default policy must provide no host network and no writable host path.
|
||||
6. Time, memory, process count, temporary storage, protocol, and output must be
|
||||
bounded independently.
|
||||
7. Results must distinguish an observed return, an observed exception or
|
||||
termination, a derived comparison, and an unresolved application-level
|
||||
claim.
|
||||
8. A reproducer must commit enough provenance to detect every relevant change
|
||||
without committing proprietary application source to the repository.
|
||||
9. CLI and MCP must expose the same plan, approval, execution, cancellation,
|
||||
failure, and Evidence semantics.
|
||||
|
||||
## Terminology
|
||||
|
||||
- **Passive observation**: bounded reads from an existing browser or Electron
|
||||
target without evaluating code, driving input, navigating, or controlling
|
||||
its lifecycle.
|
||||
- **Controlled replay**: execution of operator-selected code in a declared,
|
||||
isolated environment with committed inputs and limits. This is an Evidence
|
||||
authority, not a claim that the real application behaved identically.
|
||||
- **Extracted-module replay**: the only admitted JavaScript replay mode in v1.
|
||||
REA evaluates a finite manifest of module bytes and explicit stubs without
|
||||
launching the application.
|
||||
- **Application interaction**: launching or driving the original application,
|
||||
browser, renderer, or Electron main process. This is not extracted-module
|
||||
replay and is not authorized by this ADR.
|
||||
- **Replay plan**: the canonical, content-addressed description of one proposed
|
||||
run before execution.
|
||||
- **Replay worker**: the disposable Node.js process inside the OS sandbox. It
|
||||
is never the REA server process.
|
||||
- **Sandbox backend**: the OS mechanisms and adapter that establish namespaces,
|
||||
resource controls, filesystem visibility, and owned cleanup.
|
||||
|
||||
## Threat model
|
||||
|
||||
### Protected assets
|
||||
|
||||
The boundary protects:
|
||||
|
||||
- the REA MCP/CLI process, its memory, credentials, sockets, and Evidence
|
||||
ledger;
|
||||
- user files outside the approved source reads and all host files from writes;
|
||||
- other same-user processes and their control interfaces;
|
||||
- host network interfaces, loopback services, Unix sockets, and external
|
||||
services;
|
||||
- process, memory, CPU, temporary-storage, output, and protocol capacity;
|
||||
- the integrity of the replay plan, result, Evidence provenance, and exported
|
||||
reproducer; and
|
||||
- proprietary module bytes, which remain local and are never repository
|
||||
fixtures.
|
||||
|
||||
### Adversaries and failure sources
|
||||
|
||||
REA treats all of the following as untrusted:
|
||||
|
||||
- recovered module source, including transitive modules and source maps;
|
||||
- caller-provided JavaScript stubs;
|
||||
- structured test inputs and generated fuzz cases;
|
||||
- returned values, exceptions, console data, and worker protocol messages;
|
||||
- source paths, symbolic links, and files that can change between planning and
|
||||
execution; and
|
||||
- a worker that crashes, hangs, forks, allocates aggressively, or exits while
|
||||
descendants remain alive.
|
||||
|
||||
An operator-controlled sandbox executable, kernel, Node.js runtime, and REA
|
||||
installation are trusted computing base. A hostile host administrator, kernel
|
||||
or sandbox vulnerability, hardware side channel, and denial of service by an
|
||||
already-compromised same-user process are out of scope. These exclusions do not
|
||||
permit REA to omit the per-run resource controls in this decision.
|
||||
|
||||
### Required mitigations
|
||||
|
||||
The implementation must address:
|
||||
|
||||
| Threat | Required control |
|
||||
| -------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| Server-process compromise | Separate worker process inside the OS sandbox; no in-process fallback |
|
||||
| Host file reads | Empty sandbox root; only a minimal read-only runtime closure; module bytes enter over a private bounded channel |
|
||||
| Host file writes | No writable bind mounts; size-limited private tmpfs only |
|
||||
| Network access | Separate network namespace with no host or external connectivity; no allowlist mode in v1 |
|
||||
| Host process control | PID/user/IPC/UTS isolation, no inherited control sockets, new session, dropped capabilities |
|
||||
| Kernel escape/inspection surface | Architecture-checked seccomp denial policy in addition to namespaces, capabilities, and cgroups |
|
||||
| Fork, memory, or CPU exhaustion | Per-run cgroup v2 task, memory, swap, and CPU limits plus an outer wall-clock deadline |
|
||||
| Output/protocol exhaustion | Independent byte/message/depth limits enforced by the parent before parsing or retaining output |
|
||||
| Symlink and file-change races | Canonical authorization, descriptor-backed reads, digesting, staging, and exact plan revalidation |
|
||||
| Undeclared dependencies | Closed module manifest; exact imports only; missing/dynamic imports fail rather than use host resolution |
|
||||
| Nondeterministic globals | Versioned clock, PRNG, locale, timezone, platform, and API stubs; undeclared globals are unavailable |
|
||||
| Orphan descendants | Owned PID namespace and cgroup kill followed by an observable empty-cgroup cleanup check |
|
||||
| Forged result claims | Framed worker protocol, strict schemas, parent-computed commitments, and bounded stderr kept separate |
|
||||
| Secret leakage | Empty environment, no credential inheritance, bounded local results, and existing genuine-secret redaction |
|
||||
|
||||
## Decision
|
||||
|
||||
### 1. Add a distinct `javascript_replay` permission capability
|
||||
|
||||
JavaScript replay receives a new permission capability named
|
||||
`javascript_replay`. It does not inherit, imply, or satisfy
|
||||
`process_capture`, `browser_observe`, `electron_observe`, artifact-read, or
|
||||
workspace permissions. Grants for any of those capabilities do not satisfy a
|
||||
JavaScript replay request, and a JavaScript replay grant does not authorize
|
||||
them.
|
||||
|
||||
The administrator ceiling is disabled by default. Its eventual configuration
|
||||
must commit:
|
||||
|
||||
- canonical roots from which module and optional stub files may be read;
|
||||
- the exact Node.js and sandbox-backend executables;
|
||||
- filesystem mount authority needed only to construct the private sandbox;
|
||||
- network mode `none`; and
|
||||
- no inherited environment names.
|
||||
|
||||
Configuration establishes only the maximum authority. Every execution also
|
||||
requires an active permission grant and literal per-call approval bound to the
|
||||
exact replay plan. Project, session, and administrator grants may satisfy the
|
||||
permission-policy layer, but none replace that per-run approval.
|
||||
|
||||
Reproducer export is a separate host write after the sandbox has stopped. This
|
||||
ADR's root-scoped `evidence_write` authority was removed; current exports use
|
||||
the path supplied by the caller, while replay still waits for complete sandbox
|
||||
cleanup before writing.
|
||||
|
||||
### 2. Admit extracted modules only
|
||||
|
||||
The v1 mode executes a finite, closed manifest of JavaScript modules. The
|
||||
manifest identifies one entry module and every admitted dependency by a stable
|
||||
sandbox alias and SHA-256 digest. Imports resolve only to another manifest
|
||||
entry or to an explicitly declared, versioned stub. Bare package lookup,
|
||||
ambient `node_modules`, import maps from the host, network imports, native
|
||||
addons, WASI, FFI, child processes, workers, inspector activation, and implicit
|
||||
dynamic import are unavailable.
|
||||
|
||||
Modules and stubs are read and hashed by the authorized parent, then copied as
|
||||
bytes through a bounded private channel. Original host paths are not mounted
|
||||
into the sandbox. The worker cannot reopen the operator's source path.
|
||||
|
||||
Source maps may supply locations, but source-map source code is not executed
|
||||
unless its bytes are separately present in the approved manifest. A source-map
|
||||
name or Webpack module ordinal is never execution authority.
|
||||
|
||||
REA does not launch, attach to, navigate, click, type into, or terminate the
|
||||
user's application under this capability. A full application process remains
|
||||
an independently approved Process Capture experiment; browser and Electron
|
||||
targets remain passive. Any future mode that drives an application or permits
|
||||
host/allowlisted network requires a new ADR and a distinct permission scope.
|
||||
|
||||
### 3. Use a two-phase, content-bound approval flow
|
||||
|
||||
One public replay surface supports a plan phase and an execute phase with the
|
||||
same schema through CLI and MCP.
|
||||
|
||||
The plan phase:
|
||||
|
||||
1. validates all caller input and hard limits before file access;
|
||||
2. obtains authority to read every selected module or stub path;
|
||||
3. canonicalizes and opens each file without following an unapproved identity;
|
||||
4. reads bounded bytes, computes digests, and closes the source descriptors;
|
||||
5. resolves and probes the exact runtime and sandbox backend without running
|
||||
application code;
|
||||
6. normalizes stubs, inputs, generator settings, and limits; and
|
||||
7. returns the complete proposed effects and a canonical `plan_digest`.
|
||||
|
||||
The plan must disclose at least:
|
||||
|
||||
- module aliases, canonical local paths, byte counts, and SHA-256 digests;
|
||||
- input and stub identities, provenance, and digests;
|
||||
- runtime and sandbox executable paths, versions, and executable digests;
|
||||
- sandbox policy version and digest;
|
||||
- network `none`, visible read-only runtime paths, and private writable tmpfs
|
||||
size;
|
||||
- time, CPU, memory, swap, task, temporary-storage, input, output, protocol,
|
||||
case-count, and serialization limits;
|
||||
- deterministic clock, PRNG seed/algorithm, locale, timezone, and platform
|
||||
projection; and
|
||||
- expected Evidence and cleanup behavior.
|
||||
|
||||
Execution requires the exact `plan_digest`. REA
|
||||
rebuilds the plan immediately before admission. Any changed file, runtime,
|
||||
sandbox executable, policy, stub, input, limit, or environment commitment
|
||||
returns `plan_stale` before application code starts. Approval is not transferable
|
||||
to a different plan and cannot be represented by a bare Boolean alone.
|
||||
|
||||
Interactive CLI may print the plan and ask for confirmation. Non-interactive
|
||||
CLI and MCP must perform the same explicit plan/execute exchange; they may not
|
||||
auto-approve because the caller requested JSON output or because permission
|
||||
elicitation is supported.
|
||||
|
||||
### 4. Require an OS sandbox; fail closed elsewhere
|
||||
|
||||
The first production backend is Linux-only. It must combine:
|
||||
|
||||
- an empty mount namespace rooted in private tmpfs;
|
||||
- a new user namespace with all capabilities dropped and further nested user
|
||||
namespaces disabled;
|
||||
- new PID, network, IPC, UTS, and cgroup namespaces;
|
||||
- a new terminal session and parent-death behavior;
|
||||
- no host D-Bus, agent, browser, Docker, Wayland/X11, SSH, GPG, audio, device,
|
||||
or application sockets;
|
||||
- a minimal, read-only runtime closure rather than `/`, the user's home,
|
||||
working directory, or host `/tmp`;
|
||||
- a fresh `/proc` for the sandbox PID namespace and a minimal private `/dev`;
|
||||
- a parent-generated, architecture-checked seccomp policy that denies at least
|
||||
namespace or mount changes, `ptrace` and cross-process memory inspection,
|
||||
BPF/perf access, kernel keyring and module control, reboot/swap controls, and
|
||||
the `TIOCSTI` terminal injection path;
|
||||
- cgroup v2 `memory.max`, `memory.swap.max=0`, `pids.max`, and bounded CPU
|
||||
controls owned for the one run; and
|
||||
- an outer REA supervisor that can kill the complete cgroup and verify it is
|
||||
empty.
|
||||
|
||||
The seccomp program is passed by descriptor, has a version and digest committed
|
||||
to the replay plan, validates the syscall architecture before the syscall
|
||||
number, and never permits `ptrace`-based mediation. Seccomp reduces exposed
|
||||
kernel surface; it is not described as a complete sandbox by itself.
|
||||
|
||||
The reference backend for the implementation PR is Bubblewrap for namespace,
|
||||
mount, and seccomp construction plus a delegated cgroup v2 manager for resource
|
||||
enforcement. REA must feature-probe the actual required operations; version
|
||||
text alone is insufficient. A setuid Bubblewrap installation, a backend that
|
||||
silently skips a requested namespace or filter, unavailable user namespaces,
|
||||
missing cgroup delegation, or inability to prove cleanup makes the capability
|
||||
unavailable. REA setup must not install or upgrade Bubblewrap, systemd, Node.js,
|
||||
or kernel components.
|
||||
|
||||
macOS, Windows, Linux hosts without every required feature, containers that
|
||||
block the required namespaces, and alternative backends are unavailable in
|
||||
v1. They must return precise diagnostics and remediation, not a process-only or
|
||||
`vm`-only degraded mode.
|
||||
|
||||
Node.js runs with its permission model enabled and without grants for network,
|
||||
filesystem, child processes, workers, native addons, WASI, FFI, or inspector.
|
||||
That layer is defense in depth. It is never cited as the OS security boundary,
|
||||
because Node.js explicitly does not promise containment of malicious code.
|
||||
|
||||
### 5. Make the worker disposable and deterministic by construction
|
||||
|
||||
Each approved run receives a new sandbox, runtime root, worker process, module
|
||||
loader, and deterministic state. No module cache, realm, timer, file, global,
|
||||
or child survives into another run. Within a multi-case run, every case gets a
|
||||
fresh realm and fresh module instances; case order and generator state are
|
||||
committed.
|
||||
|
||||
The worker exposes a small versioned set of language primitives and explicitly
|
||||
selected stubs. It does not expose `process`, `require`, host `console`, timers,
|
||||
`fetch`, sockets, filesystem APIs, `Buffer`, worker APIs, native bindings, or
|
||||
ambient environment variables. Date/time, randomness, locale, timezone, and
|
||||
platform values come only from committed deterministic providers. An
|
||||
unsupported global or import produces an observed exception; REA does not
|
||||
substitute a convenient host implementation.
|
||||
|
||||
Caller-provided stubs are executable untrusted code and receive the same
|
||||
sandbox treatment as recovered modules. Built-in stubs have stable IDs,
|
||||
versions, canonical source digests, and documented semantics. No undeclared
|
||||
fallback stub is allowed.
|
||||
|
||||
Inputs are bounded canonical JSON. Results are projected without invoking
|
||||
getters, proxies, `toJSON`, or arbitrary coercion: only admitted primitives,
|
||||
arrays, and plain data properties are serialized. Cycles, accessors, functions,
|
||||
symbols, unsupported prototypes, excessive depth, excessive nodes, and
|
||||
oversized values become explicit serialization outcomes. Worker stdout is the
|
||||
framed protocol only; bounded diagnostic stderr and bounded, opt-in console
|
||||
observations remain separate.
|
||||
|
||||
### 6. Enforce independent resource ceilings
|
||||
|
||||
Every limit has a conservative product default. The v1 plan and result commit:
|
||||
|
||||
- an outer wall-clock deadline for the complete disposable run;
|
||||
- cgroup memory, swap, task, and CPU ceilings;
|
||||
- Node/V8 heap ceiling as defense in depth;
|
||||
- private tmpfs bytes;
|
||||
- serialized worker-protocol bytes, aggregate module bytes, and case-input
|
||||
bytes; preset-generated cases also have a fixed preflight count ceiling;
|
||||
- stdout, stderr, exception, stack, and aggregate retained-output bytes; and
|
||||
- structured-result depth and node count.
|
||||
|
||||
The parent enforces protocol and output limits while bytes arrive, before JSON
|
||||
parse or Evidence retention. The worker cannot request wider limits. A limit
|
||||
hit terminates the complete run cgroup and is an observed termination, not a
|
||||
partial successful return.
|
||||
|
||||
### 7. Preserve exact provenance and the right Evidence authority
|
||||
|
||||
A run that reaches worker admission produces Evidence with:
|
||||
|
||||
- provider `rea-javascript-replay` and a concrete implementation version;
|
||||
- operation `run_controlled_replay`;
|
||||
- authority `controlled-replay`;
|
||||
- confidence `observed` for the exact returned value, thrown exception,
|
||||
sandbox denial, limit termination, signal, exit, or crash that REA directly
|
||||
observed;
|
||||
- environment isolation `container`, the host platform/architecture, runtime
|
||||
identity, sandbox-backend identity, and sandbox-policy digest;
|
||||
- the replay plan digest, module and stub digests, canonical input or generated
|
||||
case digests, deterministic providers, and every effective limit;
|
||||
- bounded raw input, output, and exception data when retained, plus their
|
||||
canonical digests and truncation state;
|
||||
- start/admission/termination/cleanup states; and
|
||||
- explicit limitations for incomplete output, nondeterminism not checked,
|
||||
sandbox diagnostics, or cleanup that could not be proven.
|
||||
|
||||
The host module path remains an actionable local diagnostic and Evidence
|
||||
subject location, while the worker sees only a stable alias. Module source is
|
||||
not embedded in ordinary Evidence. A separately approved reproducer may copy
|
||||
module/stub bytes or may retain path-plus-digest references; its manifest says
|
||||
which mode was used. Repository tests use source-owned fixtures, and real-app
|
||||
verification stores only digests and expected structural facts.
|
||||
|
||||
Derived differential results use confidence `derived` and link every source
|
||||
run Evidence ID. A repeat with the same commitment can establish
|
||||
`repeat-confirmed` for those runs; a single deterministic plan is only
|
||||
`committed-not-confirmed`. Neither state proves behavior in the original
|
||||
application.
|
||||
|
||||
When replay observations are added to the JavaScript Application Graph, their
|
||||
graph-fact authority is `controlled-replay`. A claim that the real renderer,
|
||||
preload, main process, or remote service behaves the same remains inferred or
|
||||
unknown until compatible runtime authority exists. Static inference, passive
|
||||
CDP observation, controlled replay, and real application interaction are never
|
||||
promoted into one another.
|
||||
|
||||
### 8. Treat post-admission failures as observations
|
||||
|
||||
Pre-admission failures return a typed error and create no claim that module code
|
||||
ran. Once the worker is admitted, REA retains a bounded execution record even
|
||||
when the requested function does not return normally.
|
||||
|
||||
| Condition | Caller result and Evidence semantics |
|
||||
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Disabled capability or missing grant | `permission_required`/`capability_unavailable`; no worker admission and no replay observation |
|
||||
| Sandbox/runtime probe failure | `capability_unavailable` with exact failed feature; no module execution |
|
||||
| Plan digest mismatch or changed source | `plan_stale`; no module execution |
|
||||
| Sandbox setup fails before worker admission | `execution_failure`; no controlled-replay claim about the module |
|
||||
| Module returns admitted data | Observed return value with complete or explicitly limited coverage |
|
||||
| Module throws or result serialization rejects | Successful replay observation with outcome `exception` or `serialization_error`; application claim unknown |
|
||||
| Node permission or sandbox denies an operation | Observed denial/termination when attributable; no claim that all attempted side effects were enumerated |
|
||||
| Timeout, memory, CPU, task, tmpfs, or output hit | Observed limit termination; requested functional result unknown |
|
||||
| Signal, runtime crash, or malformed protocol | Observed termination plus bounded diagnostics; functional result unknown |
|
||||
| Caller cancellation after admission | Kill full cgroup, record cancellation/cleanup if possible, and project the public cancellation contract |
|
||||
| Cleanup proves an empty cgroup and removed root | `cleanup: complete` |
|
||||
| Cleanup cannot prove emptiness or removal | `cleanup_incomplete`, name every residual resource, and do not report success |
|
||||
|
||||
An exception is data, not an adapter failure. A sandbox setup error is not
|
||||
evidence that the module crashed. A timeout is not evidence that the function
|
||||
would never return. An output limit is not evidence that the truncated prefix
|
||||
equals the full result. These distinctions are part of the public contract.
|
||||
|
||||
Cancellation and cleanup must be idempotent. REA first stops admission and
|
||||
input, kills the owned cgroup, waits for an empty-cgroup observation, removes
|
||||
the private runtime root, closes protocol descriptors, and releases timers.
|
||||
Failure at one cleanup step does not skip the remaining steps.
|
||||
|
||||
### 9. Keep replay records reproducible without overstating determinism
|
||||
|
||||
The canonical replay manifest commits:
|
||||
|
||||
```text
|
||||
plan structure and policy identity
|
||||
REA/provider/runtime/sandbox executable identities and digests
|
||||
module aliases, dependency graph, bytes digests, and source references
|
||||
stub IDs, versions, source digests, and caller-provided stub digests
|
||||
canonical inputs or generator algorithm, seed, case order, and case digests
|
||||
clock, PRNG, timezone, locale, platform, and exposed-global commitments
|
||||
all resource and serialization limits
|
||||
network and filesystem projections
|
||||
expected entry export and invocation convention
|
||||
```
|
||||
|
||||
The result manifest adds canonical output/exception/termination digests,
|
||||
coverage, dropped-data counts, cleanup state, and source Evidence links.
|
||||
Canonical digests are computed by the trusted parent; worker-provided digests
|
||||
are ignored.
|
||||
|
||||
Reproduction requires exact manifest compatibility. A runtime, sandbox policy,
|
||||
built-in stub, module byte, generated case, limit, architecture, or platform
|
||||
change is a different experiment. Cross-environment comparison may still be a
|
||||
derived investigation, but it is never an exact replay.
|
||||
|
||||
### 10. Expose availability and policy truth before execution
|
||||
|
||||
`rea doctor`, capability inventory, and the planning response must report the
|
||||
JavaScript replay capability without executing application code. Diagnostics
|
||||
name the exact missing executable, unsupported host, failed namespace feature,
|
||||
cgroup delegation failure, setuid rejection, runtime mismatch, or cleanup-probe
|
||||
failure. Genuine secrets remain redacted; local executable paths, versions,
|
||||
digests, and kernel feature failures remain visible.
|
||||
|
||||
The capability must be absent from executable tool routing until implemented.
|
||||
Accepting this ADR does not increase the current tool count and does not add a
|
||||
hidden experimental flag.
|
||||
|
||||
## Required implementation and verification gate
|
||||
|
||||
The implementation PR cannot merge until all of the following are true:
|
||||
|
||||
1. `javascript_replay` exists in configuration, permission policy, project
|
||||
grants, policy explanation, CLI, MCP, docs, and capability inventory with
|
||||
fail-closed defaults.
|
||||
2. Plan and execute phases have schema-identical CLI/MCP behavior and an exact
|
||||
plan-digest stale check.
|
||||
3. No replay-disabled static, browser, Electron, session, setup, doctor, or
|
||||
application workflow can reach module execution.
|
||||
4. The worker never imports into the REA server process and no adapter offers a
|
||||
`vm`-only fallback.
|
||||
5. The Linux backend feature-probes namespaces, seccomp installation and
|
||||
denial, delegated cgroup v2 limits, and the exact admitted architecture.
|
||||
Every plan content-addresses the worker, seccomp filter, Node executable,
|
||||
ELF loader, shared-library closure, and sandbox executables.
|
||||
6. Source-owned fixtures cover parser, sanitizer, clipboard, exception,
|
||||
ambient side-effect attempts, memory pressure, and busy loops. The worker
|
||||
exposes no filesystem, environment, network, process, worker, native-addon,
|
||||
timer, inspector, or ambient package-resolution API.
|
||||
7. Tests and the real verifier prove bounded protocol/diagnostics, timeout and
|
||||
OOM cgroup termination, and observed cgroup cleanup.
|
||||
8. Evidence tests cover observed return/exception/termination, derived
|
||||
comparison with source Evidence links, stale admission, cancellation
|
||||
projection, and explicit limitations on application-level claims.
|
||||
9. Package verification exercises the target-free public contract. The
|
||||
dedicated real-Linux workflow exercises the sandbox backend; environments
|
||||
without delegated user namespaces or cgroups report truthful unavailability
|
||||
rather than selecting a weaker backend.
|
||||
10. A real operator-local Notion-like parser or sanitizer benchmark can run
|
||||
without committing its source, and exports only approved local reproducer
|
||||
material.
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
### Reuse `browser_observe`, `electron_observe`, or `process_capture`
|
||||
|
||||
Rejected because those grants authorize different effects. Reuse would turn a
|
||||
passive or unsandboxed process permission into recovered-code execution.
|
||||
|
||||
### Execute in the MCP server with `vm.runInContext`
|
||||
|
||||
Rejected because a VM context is not a security boundary and a crash, resource
|
||||
attack, or escape would compromise the long-lived session and its credentials.
|
||||
|
||||
### Use only the Node.js permission model
|
||||
|
||||
Rejected because Node.js documents it as a seat belt for trusted code and
|
||||
explicitly does not guarantee containment of malicious code. It remains a
|
||||
defense-in-depth layer inside the OS sandbox.
|
||||
|
||||
### Ship a process-only fallback on unsupported hosts
|
||||
|
||||
Rejected because it changes the security claim while preserving the same tool
|
||||
name. Unsupported hosts return unavailable.
|
||||
|
||||
### Permit network allowlists in v1
|
||||
|
||||
Rejected because DNS, redirects, proxies, credentials, service identity, and
|
||||
response nondeterminism would substantially expand the threat and Evidence
|
||||
model. The v1 namespace has no host or external network.
|
||||
|
||||
### Mount the approved project root read-only
|
||||
|
||||
Rejected because one approved module does not imply authority to disclose
|
||||
every file in its project. The parent stages only exact, digested bytes.
|
||||
|
||||
### Treat exceptions, crashes, or limit hits as ordinary tool errors only
|
||||
|
||||
Rejected because that discards the principal behavioral observation and makes
|
||||
negative results impossible to reproduce. Post-admission termination is
|
||||
retained with explicit functional uncertainty.
|
||||
|
||||
## Consequences
|
||||
|
||||
- JavaScript replay is Linux-only at first and may be unavailable on containers
|
||||
or distributions that disable user namespaces or cgroup delegation.
|
||||
- The implementation requires more host probing and lifecycle code than a
|
||||
simple Node worker, but the public security claim remains stable.
|
||||
- Plans require two calls in non-interactive environments and become stale when
|
||||
any committed input changes.
|
||||
- Network-dependent modules and full application interactions are excluded;
|
||||
their absence is explicit rather than emulated implicitly.
|
||||
- Results are reproducible for the committed isolated experiment, not proof of
|
||||
production behavior.
|
||||
- Adding another sandbox backend, network mode, or application-interaction mode
|
||||
requires a separate reviewed decision and conformance suite.
|
||||
|
||||
## References
|
||||
|
||||
- [Node.js permissions](https://nodejs.org/api/permissions.html) documents that
|
||||
the permission model does not protect against malicious code and describes
|
||||
its known filesystem and cross-process limitations.
|
||||
- [Bubblewrap README](https://github.com/containers/bubblewrap/blob/main/README.md)
|
||||
describes its namespace construction and makes clear that the caller's
|
||||
arguments, not Bubblewrap alone, define the sandbox security model.
|
||||
- [Linux seccomp filter documentation](https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html)
|
||||
explains that syscall filtering reduces kernel surface but is not a complete
|
||||
sandbox by itself.
|
||||
- [ADR-0001](0001-provider-selection-and-analysis-profiles.md) establishes the
|
||||
existing rule that provider selection, Evidence provenance, and capability
|
||||
unavailability remain explicit and never silently fall back.
|
||||
@@ -1,452 +0,0 @@
|
||||
# ADR-0003: Managed-code evidence and provider boundary
|
||||
|
||||
> Historical design record: The non-executing runtime-correlation planning
|
||||
> tool described in earlier revisions was removed. This ADR's current
|
||||
> implementation status below covers only the static managed tools that ship.
|
||||
|
||||
- Status: Superseded for runtime planning; static evidence boundary remains in force
|
||||
- Date: 2026-07-16
|
||||
- Implementation status: Read-only PE/CLI triage and exact identity are shipped
|
||||
through `inspect_managed_artifact` / `rea inspect-managed-artifact`. Bounded
|
||||
metadata, signatures, method bodies, raw CIL hashes, limited
|
||||
decoded-instruction-tuple v1 hashes, exception regions, call edges, and
|
||||
field-access anchors are shipped through `inspect_managed_members`; `rea
|
||||
inspect-managed-members` provides CLI parity. Static member comparison and build-local
|
||||
token remapping are shipped through `compare_managed_members` /
|
||||
`rea compare-managed-members`. Declaration-only managed/native boundary
|
||||
inventory for ModuleRef, ImplMap/PInvoke, ReadyToRun indicators, and non-IL
|
||||
method flags is shipped through `inspect_managed_native_boundaries` /
|
||||
`rea inspect-managed-native-boundaries`. Managed/native export or function
|
||||
Evidence matching is shipped through `verify_managed_native_boundaries` /
|
||||
`rea verify-managed-native-boundaries`; it verifies only supplied P/Invoke
|
||||
export/function observations and does not map managed tokens to native
|
||||
addresses. Decompiler reconstruction import is
|
||||
shipped through `import_managed_reconstruction` /
|
||||
`rea import-managed-reconstruction`; it records user-supplied C#/IL/pseudocode
|
||||
as analyst inference only after exact static member locks match. Managed
|
||||
inspection does not execute assemblies or observe CLR internals. Managed static graph
|
||||
projection is shipped through
|
||||
`project_managed_application_graph` /
|
||||
`rea project-managed-application-graph`; it projects authenticated managed
|
||||
Evidence into the application graph without changing managed tokens into
|
||||
native addresses. Native-body bridge mapping and an actual runtime executor
|
||||
remain future contracts.
|
||||
|
||||
## Context
|
||||
|
||||
Professional C# reverse engineering is not one decompiler operation. A file
|
||||
described as “.NET” can be ordinary Common Intermediate Language (CIL),
|
||||
ReadyToRun code containing both CIL and native implementations, a single-file
|
||||
deployment, C++/CLI mixed mode, Unity Mono, Unity IL2CPP, or NativeAOT. Those
|
||||
forms have different sources of truth and different native-analysis needs.
|
||||
Choosing a decompiler before classifying the artifact can therefore produce a
|
||||
plausible but false model.
|
||||
|
||||
The durable static facts in an ordinary managed PE are its PE/CLI headers,
|
||||
metadata tables, signatures, method bodies, exception regions, and CIL. A C#
|
||||
decompiler reconstructs source-like control flow and language constructs from
|
||||
those facts. That reconstruction is valuable, but it is not the original
|
||||
source and cannot replace the underlying metadata and CIL evidence. Obfuscation
|
||||
makes names especially weak while tokens remain coordinates in only one build.
|
||||
|
||||
REA already separates one selected deep native provider from disjoint auxiliary
|
||||
providers under [ADR-0001](0001-provider-selection-and-analysis-profiles.md).
|
||||
It also separates static evidence, passive observation, process capture, and
|
||||
interactive observation authorities. Managed analysis must fit those boundaries
|
||||
without requiring a CLR to inspect an artifact, loading target code into the
|
||||
REA process, or treating a managed token as a native address.
|
||||
|
||||
The design is informed by exact-build, reflection-free metadata extraction,
|
||||
hash-locked method manifests, independent behavior models, and managed/native
|
||||
boundary analysis performed against a local C# application. The application,
|
||||
its decompiler output, and its runtime observations are research inputs only;
|
||||
they are not distributable REA fixtures.
|
||||
|
||||
## Decision drivers
|
||||
|
||||
1. Classify the deployment before selecting an analysis route.
|
||||
2. Make metadata and CIL independently inspectable without a CLR or target
|
||||
execution.
|
||||
3. Preserve exact-build provenance while enabling structural comparison across
|
||||
builds whose MVIDs, tokens, names, and layout differ.
|
||||
4. Compose managed and native analysis without introducing another ambiguous
|
||||
deep-provider candidate.
|
||||
5. Keep decompiled C# useful but epistemically subordinate to canonical bytes.
|
||||
6. Analyze Linux, macOS, and Windows managed artifacts through the same local
|
||||
CLI/MCP contracts on REA's supported hosts.
|
||||
7. Keep setup additive and prevent managed analysis from installing .NET,
|
||||
ILSpy, an SDK, or another unrelated toolchain.
|
||||
8. Prove semantics with source-built conformance artifacts and compact,
|
||||
operator-local real-application checks.
|
||||
|
||||
## Terminology
|
||||
|
||||
- **Managed artifact**: a PE, bundle component, or related metadata artifact
|
||||
that carries a CLI header, CLI metadata, CIL, or another managed-runtime
|
||||
commitment.
|
||||
- **Classification vector**: independently observed container, runtime family,
|
||||
implementation form, and architecture. It is not one guessed label.
|
||||
- **Canonical static observation**: a bounded value decoded directly from the
|
||||
target bytes under a versioned parser contract.
|
||||
- **Reconstruction**: source-like output, including decompiled C#, derived from
|
||||
canonical observations.
|
||||
- **Structural inference**: a relationship or cross-version match derived from
|
||||
signatures, APIs, constants, control/data-flow shape, or other observations.
|
||||
- **Validation**: an independently checked proposition, for example a
|
||||
source-built oracle assertion or a behavior-model corpus result.
|
||||
- **Runtime observation**: a separately authorized fact obtained by attaching,
|
||||
loading, debugging, instrumenting, or otherwise executing code.
|
||||
|
||||
## Decision
|
||||
|
||||
### 1. Classify before analyzing
|
||||
|
||||
Managed triage returns a classification vector and the observations supporting
|
||||
each dimension. Absence of a marker produces `unknown` or `not_applicable`, not
|
||||
an invented runtime family.
|
||||
|
||||
| Dimension | Initial values |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| Container | PE file, single-file bundle, bundle component, Unity metadata pair, native image, malformed, unknown |
|
||||
| Runtime family | .NET Framework, modern .NET, Unity Mono, Unity IL2CPP, NativeAOT, mixed CLR/native, unknown |
|
||||
| Implementation form | CIL, CIL plus ReadyToRun, native only, C++/CLI mixed, metadata only, unsupported, unknown |
|
||||
| Architecture | PE machine plus CLI flags; component-specific architecture for bundles; conflicting or unknown when required |
|
||||
|
||||
Target-framework attributes, assembly references, metadata stream versions,
|
||||
bundle manifests, ReadyToRun directories, and native headers are observations.
|
||||
They may support a runtime-family inference, but no single string is treated as
|
||||
authoritative when the other bytes conflict.
|
||||
|
||||
Classification selects these routes:
|
||||
|
||||
| Observed form | Managed route | Native route and limitation |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Ordinary Framework or modern CIL | Full metadata, signatures, method bodies, and CIL | Optional only for declared interop boundaries |
|
||||
| Obfuscated CIL | Same canonical route; names receive no special trust | Optional for declared interop boundaries |
|
||||
| Unity Mono assembly | Same canonical route with Unity context retained | Optional for engine/native boundaries |
|
||||
| ReadyToRun | Metadata/CIL where present; implementation availability is per method | Selected Hopper/Ghidra analyzes native sections; CIL absence is reported per method |
|
||||
| C++/CLI mixed mode | Metadata/CIL for managed members and explicit mixed-mode boundary observations | Selected Hopper/Ghidra analyzes native bodies; REA never converts tokens directly to native addresses |
|
||||
| Single-file deployment | Inventory the bundle, then analyze each authenticated managed component | Selected Hopper/Ghidra analyzes the host/native components; unsupported compression remains explicit |
|
||||
| Unity IL2CPP | Pair and authenticate available Unity metadata; do not claim canonical CIL | Selected Hopper/Ghidra analyzes the generated native binary; semantic recovery is degraded until supported |
|
||||
| NativeAOT | Report any independently present managed metadata, otherwise no CIL claim | Treat as a native target with NativeAOT provenance; managed source reconstruction is unavailable |
|
||||
| Malformed or unsupported artifact | Return bounded partial observations and typed unavailable/unknown scopes | Do not silently retry by loading or executing the file |
|
||||
|
||||
Classification is byte-driven and bounded. File extensions, product names,
|
||||
process names, and an operator's description are hints only.
|
||||
|
||||
### 2. Implement managed static analysis as a disjoint auxiliary provider
|
||||
|
||||
The initial producer is a REA-owned auxiliary provider with ID
|
||||
`rea-dotnet-static`. It owns a disjoint managed-artifact operation family. It is
|
||||
not registered as a Hopper/Ghidra deep-provider candidate because it does not
|
||||
compete for the same operation names and must be able to coexist with the one
|
||||
selected native provider.
|
||||
|
||||
```text
|
||||
one opened artifact/session
|
||||
|
|
||||
SessionProviderRouter
|
||||
/ \
|
||||
rea-dotnet-static auxiliary selected deep binding
|
||||
PE/CLI metadata and CIL Hopper or Ghidra
|
||||
\ /
|
||||
explicit managed/native links
|
||||
|
|
||||
Evidence and workflows
|
||||
```
|
||||
|
||||
An ordinary managed artifact can therefore remain unbound to a deep native
|
||||
provider while managed operations work. ReadyToRun, C++/CLI, IL2CPP, NativeAOT,
|
||||
and interop workflows may additionally require a caller-selected native
|
||||
binding. Native-provider failure never causes managed evidence to be relabeled,
|
||||
and managed-parser failure never causes an implicit native fallback.
|
||||
|
||||
Provider mechanics live under a provider directory such as `src/dotnet/`.
|
||||
Provider-neutral identities, evidence states, and bounded values may live in
|
||||
the domain/contracts layers. Application code composes results but does not
|
||||
parse PE headers, metadata tables, signatures, CIL, ReadyToRun headers, or
|
||||
provider-specific output.
|
||||
|
||||
### 3. Make the production static path byte-only and execution-free
|
||||
|
||||
`rea-dotnet-static` reads approved local bytes and implements the required
|
||||
PE/CLI metadata and CIL subset in TypeScript. It does not call any reflection
|
||||
API and does not require the .NET runtime. In particular, static inspection
|
||||
must never:
|
||||
|
||||
- call `Assembly.Load`, `Assembly.LoadFrom`, `ReflectionOnlyLoad`, or an
|
||||
equivalent runtime loader;
|
||||
- resolve target dependencies by executing target-controlled resolver code;
|
||||
- run module initializers, constructors, entry points, build tasks, plugins, or
|
||||
generated helpers;
|
||||
- start the target, attach to a process, contact a service, or inherit target
|
||||
configuration and credentials; or
|
||||
- interpret a decompiler, CLR, or native-provider diagnostic as an observation
|
||||
from the artifact bytes.
|
||||
|
||||
The parser uses checked offsets and additions, explicit endianness, bounded
|
||||
table/heap/body counts, cycle-safe traversal, and typed partial results. A bad
|
||||
row, signature, body, resource, or stream does not authorize an unbounded retry
|
||||
or a CLR load. Complete-coverage claims require every applicable bounded region
|
||||
to have been admitted; otherwise coverage is partial, unknown, or unavailable
|
||||
with exact offsets and reasons.
|
||||
|
||||
The caller selects the local input path directly. Local artifact
|
||||
paths, digests, MVIDs, tokens, malformed offsets, and analysis metadata are
|
||||
actionable diagnostics and are not secrets. Credentials, authorization data,
|
||||
and genuine secrets remain redacted.
|
||||
|
||||
### 4. Bind every finding to exact artifact and producer identity
|
||||
|
||||
Every managed result carries a commitment with at least:
|
||||
|
||||
```text
|
||||
artifact: canonical path + byte length + SHA-256
|
||||
component: container identity + component path/name + component SHA-256, when applicable
|
||||
classification: container + runtime family + implementation form + architecture
|
||||
assembly/module: simple name + version + culture + public-key identity + module name + MVID
|
||||
producer: provider ID + exact version + analysis-affecting parameters + profile digest
|
||||
```
|
||||
|
||||
An assembly-qualified name does not replace the artifact digest or MVID.
|
||||
Neither an MVID nor an Authenticode signature proves who authored the behavior.
|
||||
Bundle components retain both their container and component commitments so that
|
||||
bytes extracted from another host cannot be silently substituted.
|
||||
|
||||
Method observations add:
|
||||
|
||||
```text
|
||||
declaring type + normalized CLI signature
|
||||
build-local metadata token
|
||||
RVA/file extent when present
|
||||
raw CIL byte length + SHA-256 when the body is admitted
|
||||
decoded-instruction-tuple SHA-256 when CIL is valid
|
||||
separate max-stack, locals-token, and exception-region observations
|
||||
```
|
||||
|
||||
The shipped `il_sha256` commits only the raw CIL byte stream identified by the
|
||||
method header's `il_size`. It does not commit the method header, local-signature
|
||||
blob, alignment padding, or extra sections such as exception clauses. Those
|
||||
facts remain separate observations and the enclosing artifact SHA-256 binds
|
||||
them to the exact file. A digest over the complete method body is not shipped.
|
||||
|
||||
The caller-visible field remains named `normalized_il_sha256`, but its shipped
|
||||
v1 meaning is deliberately narrower than a canonical semantic CIL identity.
|
||||
For a completely decoded method, REA serializes the instruction-order array of
|
||||
`[opcode, operand_kind, operand]` tuples with JavaScript `JSON.stringify`,
|
||||
encodes that text as UTF-8, and reports its lowercase SHA-256. The tuple
|
||||
projection is defined here; the member result carries only the described
|
||||
normalized digest.
|
||||
|
||||
Operands retain the current decoder projection: metadata and user-string
|
||||
operands are build-local lowercase hexadecimal tokens, scalar branch operands
|
||||
are decimal target CIL byte offsets, and `switch` retains only its decimal case
|
||||
count. Constants and variable indexes are decimal strings; an operand-free
|
||||
instruction uses kind `none` and value `null`. Opcode names preserve distinct
|
||||
short forms and prefix instructions, but instruction offsets, branch target
|
||||
instruction identity, switch targets, resolved token identities, method
|
||||
headers, locals, and exception regions are not part of this digest. A partial
|
||||
or malformed decode has no `normalized_il_sha256`.
|
||||
|
||||
Consequently v1 is a deterministic decoded-tuple fingerprint, not proof of
|
||||
semantic equivalence. In particular, methods with the same opcodes and switch
|
||||
case counts can share it despite different switch targets, and token movement
|
||||
can change it without a semantic change. Cross-build comparison may use it as
|
||||
one unique-only structural tier while retaining artifact/MVID commitments and
|
||||
ambiguity; it must not describe the digest itself as token-remapped canonical
|
||||
CIL. Canonical operand resolution, complete control-flow operands,
|
||||
exception/local commitments, and an explicit algorithm version require a new
|
||||
normalized-CIL schema.
|
||||
|
||||
Metadata tokens are reported because they are precise coordinates inside one
|
||||
module. Cross-version identity never consists of a token alone. Matching tries
|
||||
exact CIL/signature identity, then an exact declared-type/name/signature tuple,
|
||||
then structural shape; a name by itself never selects a pair. Partial or
|
||||
malformed bodies leave body facets unknown after a signature tuple pairs the
|
||||
methods. Matches cite both build commitments, both local tokens, and all
|
||||
competing candidates. V1 cannot cite a separate matching-algorithm version
|
||||
because none is exposed.
|
||||
|
||||
### 5. Separate five evidence layers
|
||||
|
||||
Managed workflows retain these layers independently:
|
||||
|
||||
| Layer | Examples | Permitted claim |
|
||||
| ---------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| Static observation | Metadata row, signature, CIL opcode, literal, exception region | “These bounded bytes decode to this value.” |
|
||||
| Reconstruction | Decompiled C#, async/iterator source shape, expression simplification | “This is one source-like representation of cited observations.” |
|
||||
| Structural inference | Likely caller, field flow, behavior slice, cross-build method match | “These cited structures support this relationship.” |
|
||||
| Independent validation | Synthetic oracle, corpus result, second parser agreement | “This declared proposition passed the named independent check.” |
|
||||
| Runtime observation | Exact-build attach/load/instrumentation event | “This separately authorized experiment observed this event.” |
|
||||
|
||||
Each layer has its own producer, authority, state, confidence, coverage, and
|
||||
limitations. A readable C# method is never emitted as original source or exact
|
||||
semantic proof. A validator does not upgrade unrelated claims. Runtime
|
||||
agreement does not retroactively turn a static inference into an observation.
|
||||
|
||||
Negative results are scope-qualified. For example, “no declared P/Invoke rows
|
||||
were found in the admitted metadata tables” does not prove the application has
|
||||
no native, dynamically resolved, generated, server-side, or protected path.
|
||||
|
||||
### 6. Admit ILSpy only as an optional reconstruction and conformance tool
|
||||
|
||||
REA's canonical static operations do not depend on ILSpy, a .NET SDK/runtime,
|
||||
dnlib, or Mono.Cecil. The following review was current on the decision date:
|
||||
|
||||
| Tool | Reviewed coordinate | License | Runtime/host observation | Decision |
|
||||
| ---------------------------- | ------------------- | ------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `System.Reflection.Metadata` | 10.0.10 | MIT | Library targets .NET Framework 4.6.2, .NET 8/9/10, and .NET Standard 2.0 | Pinned independent metadata oracle for conformance; not shipped or invoked by production parsing |
|
||||
| `ICSharpCode.Decompiler` | 10.1.1.8388 | MIT | Library targets .NET Standard 2.0; output is reconstruction | Pinned reconstruction/differential oracle candidate |
|
||||
| `ilspycmd` | 9.1.0.7988 | MIT | Cross-platform .NET 8 tool; newer 10.1.1.8388 requires .NET 10 | Initial optional BYO reconstruction coordinate because it matches the source research baseline |
|
||||
| dnlib | 4.5.0 | MIT | Cross-platform library; also exposes assembly-writing APIs | Differential test candidate only; its writable model is outside the production trust boundary |
|
||||
| Mono.Cecil | 0.11.6 | MIT | Cross-platform .NET Framework/.NET Standard library with read/write support | Differential test candidate only; not a production dependency |
|
||||
|
||||
Coordinates and package hashes used by real verification are checked into a
|
||||
source-owned lock manifest when that verifier is implemented. “Latest” is
|
||||
never a reproducibility coordinate. A version change requires conformance and
|
||||
an explicit profile/version change.
|
||||
|
||||
Artifact platform and REA host support are separate. The byte-only parser must
|
||||
inspect PE/CLI artifacts produced for Windows on a supported Linux or macOS REA
|
||||
host; that is not a claim that REA itself currently runs on Windows. Managed
|
||||
library/tool candidates above are cross-platform at their reviewed targets, but
|
||||
native composition remains limited by the selected Hopper/Ghidra host and
|
||||
format support. Real verification runs the canonical path on Linux and macOS
|
||||
and may run pinned oracle checks on Windows without turning that development
|
||||
check into a public Windows-host claim.
|
||||
|
||||
If a caller-visible C# reconstruction operation is admitted, it is a distinct
|
||||
optional capability. It accepts only an exact supported BYO `ilspycmd`, records
|
||||
the tool and decompiler version, runs in an owned bounded process without
|
||||
loading target code into REA, and links output to the canonical method
|
||||
observation. Setup and installers do not download or install ILSpy, .NET, an
|
||||
SDK, or a runtime. Doctor may inspect an explicitly configured executable and
|
||||
report exact actionable incompatibility. Missing reconstruction never removes
|
||||
canonical metadata/CIL capabilities.
|
||||
|
||||
This decision does not make a decompiler process a security sandbox. The
|
||||
initial public path can defer C# reconstruction while canonical contracts and
|
||||
process isolation are proven.
|
||||
|
||||
### 7. Use source-built conformance and operator-local real targets
|
||||
|
||||
The repository stores source, build descriptions, expected semantic facts,
|
||||
and tool lock manifests, but not compiled managed binaries. An isolated build
|
||||
step creates fixtures outside source directories and records their SHA-256 and
|
||||
MVID before verification. At minimum, the corpus covers:
|
||||
|
||||
- Framework-compatible and modern .NET metadata;
|
||||
- AnyCPU, x86, and x64 PE/CLI flags;
|
||||
- generics, nested types, overloaded signatures, properties, events, resources,
|
||||
attributes, and constants;
|
||||
- exceptions, filters, prefixes, switch branches, P/Invoke, async and iterator
|
||||
state machines;
|
||||
- obfuscated and Unicode names without assigning semantics from names;
|
||||
- malformed headers, tables, heaps, signatures, bodies, and exception regions;
|
||||
and
|
||||
- two semantically related builds with different MVIDs, tokens, layout, and
|
||||
selected names.
|
||||
|
||||
Expected facts are checked directly and against pinned
|
||||
`System.Reflection.Metadata`; ILSpy, dnlib, or Mono.Cecil may add differential
|
||||
coverage but do not vote by majority. Disagreement is a failing or explicitly
|
||||
unsupported case, not silently normalized output.
|
||||
|
||||
The real-application benchmark is an operator-local, opt-in osu! check derived
|
||||
from the research workflow. It accepts an explicit path plus a compact
|
||||
expectation manifest; verifies target SHA-256, MVID, architecture, and expected
|
||||
assembly identity before analysis; and emits only bounded identities, tokens,
|
||||
signatures, IL lengths/hashes, match states, and assertion results. It does not
|
||||
emit full IL, decompiled source, runtime logs, user data, credentials, or
|
||||
service/account material. No proprietary binary, hash-locked application
|
||||
manifest, or derived dump is committed to REA.
|
||||
|
||||
### 8. Keep runtime behavior separate from static managed analysis
|
||||
|
||||
No managed runtime planning or execution tool ships. Managed inspection does
|
||||
not execute assemblies or establish which methods ran. Where supported, direct
|
||||
process capture can record target-level inputs and outputs, but it does not
|
||||
prove CLR method execution or resolution. Internal managed behavior remains
|
||||
unknown until independently observed with suitable runtime evidence.
|
||||
|
||||
## Public contract consequences
|
||||
|
||||
- Managed static operations will have CLI and MCP parity and produce the same
|
||||
Evidence commitments.
|
||||
- Adding managed operations changes the caller-visible tool inventory and
|
||||
generated product catalog only in the implementation PR that admits them.
|
||||
- Partial parsing includes admitted/dropped counts, exact limitations, and
|
||||
coverage; it never returns a partial list labeled complete.
|
||||
- Managed addresses distinguish file offsets, RVAs, native virtual addresses,
|
||||
metadata tokens, and CIL offsets. They are not interchangeable.
|
||||
- Cache compatibility is exact on artifact/component commitment, member-result
|
||||
fields, parser profile, and producer version. For shipped v1,
|
||||
`normalized_il_sha256` has no independent algorithm-version field.
|
||||
- Provider-neutral workflows may compose canonical managed findings with the
|
||||
one native binding, but every source operation retains its actual producer.
|
||||
|
||||
## Alternatives rejected
|
||||
|
||||
### Make ILSpy the only managed provider
|
||||
|
||||
This would make canonical metadata/IL inventory depend on a separately
|
||||
installed CLR and conflate source reconstruction with byte observations. It
|
||||
would also make tool-version drift part of ordinary inventory. ILSpy remains a
|
||||
valuable optional reconstruction and conformance tool instead.
|
||||
|
||||
### Use `Assembly.Load` or reflection-only loading
|
||||
|
||||
Both delegate parsing and dependency behavior to a CLR and make static support
|
||||
runtime-dependent. Ordinary loading can execute target code; reflection-only
|
||||
loading still establishes a CLR load boundary and is unavailable across modern
|
||||
runtimes in a uniform way. Neither is required to decode ECMA-335 bytes.
|
||||
|
||||
### Register the managed analyzer as another deep candidate
|
||||
|
||||
That would force callers to choose managed analysis instead of Hopper/Ghidra
|
||||
for ReadyToRun and mixed-mode artifacts even though both are needed. Disjoint
|
||||
auxiliary composition represents the real ownership boundary.
|
||||
|
||||
### Treat tokens, names, or decompiler output as durable identity
|
||||
|
||||
Tokens and obfuscated names change across builds, while decompiler output
|
||||
changes across tools and versions. They remain useful build-local coordinates
|
||||
or reconstruction labels but are insufficient for exact or structural
|
||||
cross-version identity.
|
||||
|
||||
### Package a .NET runtime or mutable managed library in REA
|
||||
|
||||
This would expand installer effects, package size, patching obligations, and
|
||||
the target-parsing attack surface. The initial byte parser keeps canonical
|
||||
support within REA's existing Node runtime; external managed tools remain BYO
|
||||
or development-only oracles.
|
||||
|
||||
## Rollout gates
|
||||
|
||||
1. Add read-only triage and identity with malformed-input bounds. Shipped.
|
||||
2. Add metadata, signatures, method bodies, deterministic decoded-CIL tuple
|
||||
fingerprints, and exact artifact Evidence. Shipped with the v1 limitations
|
||||
above; full normalized-CIL semantics remain planned.
|
||||
3. Add obfuscation-resistant slices and cross-build match states. Shipped.
|
||||
4. Compose explicit managed/native boundaries with Hopper/Ghidra. Declaration
|
||||
inventory and native export/function Evidence matching shipped; native-body
|
||||
bridge mapping remains planned.
|
||||
5. Add source-built, pinned real-tool, package, CLI, and MCP conformance.
|
||||
Source-owned conformance is shipped; pinned external real-tool checks remain
|
||||
planned.
|
||||
6. Project authenticated managed static Evidence into the application graph.
|
||||
Shipped.
|
||||
|
||||
Every gate must keep unsupported ReadyToRun, NativeAOT, IL2CPP, mixed-mode, or
|
||||
bundle cases explicit rather than broadening claims to make a fixture pass.
|
||||
|
||||
## References
|
||||
|
||||
- [ECMA-335: Common Language Infrastructure](https://ecma-international.org/publications-and-standards/standards/ecma-335/)
|
||||
- [.NET ReadyToRun deployment overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run)
|
||||
- [.NET Native AOT deployment overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/)
|
||||
- [.NET single-file deployment overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview)
|
||||
- [`System.Reflection.Metadata` package](https://www.nuget.org/packages/System.Reflection.Metadata/10.0.10)
|
||||
- [ILSpy repository and releases](https://github.com/icsharpcode/ILSpy)
|
||||
- [dnlib repository](https://github.com/0xd4d/dnlib)
|
||||
- [Mono.Cecil repository](https://github.com/jbevain/cecil)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Architecture decision records
|
||||
|
||||
Accepted decisions describe the architecture that implementation PRs must
|
||||
follow. Acceptance does not by itself mean the behavior is shipped; each record
|
||||
states its implementation status separately.
|
||||
|
||||
| ADR | Status | Implementation |
|
||||
| ------------------------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [0001: Provider selection and analysis profiles](0001-provider-selection-and-analysis-profiles.md) | Accepted | Provider selection and 25 read-only Ghidra operations on Linux/macOS and experimental Windows x64 P0; Linux/macOS session annotations |
|
||||
| [0002: Controlled JavaScript replay authority and sandbox policy](0002-controlled-replay-authority-and-sandbox.md) | Superseded | Historical design; the controlled JavaScript replay tool was removed |
|
||||
| [0003: Managed-code evidence and provider boundary](0003-managed-code-evidence-and-provider-boundary.md) | Partially superseded | Seven static managed tools remain; runtime planning removed |
|
||||
@@ -1,177 +0,0 @@
|
||||
# Static Android APK analysis
|
||||
|
||||
REA wraps an existing headless JADX engine through `src/android/`. Its source is
|
||||
kept as an unmodified, commit-pinned Git submodule; see
|
||||
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/README.md). CLI and MCP use the same
|
||||
application workflow and return artifact-bound Evidence inline.
|
||||
|
||||
This family is present on repository main and in npm 4.1.0. Check the
|
||||
[released package boundary](installation.md#released-package-and-main) and the
|
||||
connected server's tool list before selecting it.
|
||||
|
||||
## Supply tools
|
||||
|
||||
Use a **full JDK 17 or newer** (including its compiler module) and
|
||||
**jadx-headless-mcp 0.7.1**. REA runs its packaged Java metadata bridge in
|
||||
source-file mode against the unmodified engine JAR. REA does not install Java,
|
||||
an Android SDK, JADX, a device service or an emulator. Set the path in the shell
|
||||
or MCP server environment:
|
||||
|
||||
```sh
|
||||
export REA_JADX_MCP_JAR=/absolute/path/jadx-headless-mcp-0.7.1-all.jar
|
||||
# Optional: select an existing JDK rather than java on PATH.
|
||||
export JAVA_HOME=/absolute/path/existing-jdk
|
||||
```
|
||||
|
||||
The current metadata bridge is verified on macOS arm64 with OpenJDK 21 and the
|
||||
public Appium ApiDemos fixture. The POSIX adapter also supports Linux; the new
|
||||
bridge has not yet undergone real verification on Linux/JDK 25.
|
||||
Windows is unsupported for this provider's owned stdio process boundary.
|
||||
|
||||
## Answer an analyst question
|
||||
|
||||
```sh
|
||||
rea inspect-android-package /path/Example.apk
|
||||
rea search-android-classes /path/Example.apk MainActivity
|
||||
rea inspect-android-class /path/Example.apk example.MainActivity
|
||||
rea inspect-android-method /path/Example.apk example.MainActivity onCreate
|
||||
rea inspect-android-method /path/Example.apk example.MainActivity select --overload-index 1
|
||||
rea trace-android-references /path/Example.apk example.MainActivity
|
||||
rea trace-android-references /path/Example.apk example.MainActivity --method-name onCreate
|
||||
```
|
||||
|
||||
Equivalent MCP methods are `inspect_android_package`, `search_android_classes`,
|
||||
`inspect_android_class`, `inspect_android_method` and `trace_android_references`.
|
||||
Every request includes `path`. Class/method operations use `class_name`,
|
||||
`method_name` and optional `overload_index` as applicable. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_android_method",
|
||||
"arguments": {
|
||||
"path": "/path/Example.apk",
|
||||
"class_name": "example.MainActivity",
|
||||
"method_name": "select",
|
||||
"overload_index": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `inspect_android_class` to choose an overload. REA rejects an ambiguous
|
||||
method request rather than silently selecting the first match. The engine's
|
||||
method reference operation cannot select overloads, so REA reports that boundary
|
||||
as unsupported when multiple same-name methods exist.
|
||||
|
||||
The engine's smali fallback also joins same-name overloads; REA rejects that
|
||||
fallback for an overloaded method instead of attributing all bodies to one
|
||||
selected overload. Native/abstract methods with no decompiled body report
|
||||
`body_status: "not_available"` without inferring which implementation flag caused it.
|
||||
|
||||
## Interpret the result
|
||||
|
||||
- Package inspection retains decoded manifest XML, summary declarations,
|
||||
permissions and class/resource counts. Summary fields are extracted by the
|
||||
upstream engine and should be checked against the retained XML when needed.
|
||||
Signature verification is explicitly `not_performed`.
|
||||
- Class search consumes all pages and checks their counts. An empty `query`
|
||||
inventories all class names; class names are decompiler representations.
|
||||
- Class inventories read parsed metadata without generating source and return
|
||||
full reported display types, fields and inner class names. Synthetic members
|
||||
that code generation hides can appear in the inventory; it stays consistent
|
||||
after method decompilation within the retained session.
|
||||
Exact DEX descriptors are unknown. Overload indices are tied to this artifact
|
||||
and engine, not stable identities across different builds.
|
||||
- Method inspection reports Java or smali, fallback markers and complete/partial
|
||||
returned text. Decompiled source is a derived representation. A complete text
|
||||
result does not establish complete semantics or a byte-identical reconstruction.
|
||||
- Reference results are incoming static relationships between provider nodes.
|
||||
Source lines and DEX instruction offsets remain unknown. They are not observed
|
||||
runtime calls.
|
||||
- Original requests, APK SHA-256, actual engine JAR SHA-256 and raw producer
|
||||
responses remain in Evidence. Source revision is populated only for bytes
|
||||
matching the audited release JAR.
|
||||
|
||||
These operations do not execute the APK or provide split APK/AAB handling,
|
||||
signature validation, full resource-table semantics, native-library analysis or
|
||||
Android runtime capture. Existing artifact inventory/extraction tools can supply
|
||||
archive evidence. Project that Evidence with the execution-free inventory tool:
|
||||
|
||||
```sh
|
||||
rea project-android-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "project_android_application_graph",
|
||||
"arguments": {
|
||||
"inventory_evidence": ["<inventory_artifact Evidence>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The projection reports exact component paths and hashes, runtime-family hints,
|
||||
and path-based bridge hypotheses. It does not decode DEX or claim observed
|
||||
runtime calls.
|
||||
|
||||
## Resource and lifecycle limits
|
||||
|
||||
REA retains one serialized session with private, immutable APK, engine JAR and
|
||||
metadata-bridge copies. Matching requests reuse the loaded engine. APK/engine
|
||||
digests and JVM configuration are checked before reuse; changed inputs retire
|
||||
the previous session. Sessions are cleaned after 60 seconds of inactivity, on
|
||||
target/configuration changes, failure, timeout, cancellation, MCP disconnect or
|
||||
server shutdown. One-shot CLI commands always join cleanup before returning.
|
||||
REA never writes to the original APK or launches target code.
|
||||
|
||||
If process cleanup cannot be confirmed, REA retains the workspace and reports its
|
||||
location. That provider instance blocks subsequent engine launches until the
|
||||
caller resolves the reported resources and starts a fresh instance.
|
||||
|
||||
The provider uses one JADX analysis worker independently of JVM processor and
|
||||
garbage-collector settings. REA supplies no default heap or visible-CPU override;
|
||||
Java uses its normal ergonomics and inherits caller JVM settings. Optional
|
||||
provider settings add explicit JVM arguments:
|
||||
|
||||
```sh
|
||||
export REA_JADX_HEAP_MIB=8192
|
||||
# Optional, independent of the single JADX worker:
|
||||
export REA_JADX_ACTIVE_PROCESSOR_COUNT=4
|
||||
# Or select settings through Java's standard environment options:
|
||||
export JAVA_TOOL_OPTIONS=-Xmx8g
|
||||
```
|
||||
|
||||
Both REA settings must be positive safe integers; processor counts must also fit
|
||||
the JVM's signed 32-bit range. REA preserves
|
||||
`JAVA_TOOL_OPTIONS`, `JDK_JAVA_OPTIONS` and `_JAVA_OPTIONS` rather than replacing
|
||||
them. Java's standard precedence applies: explicit command-line heap settings
|
||||
override `JAVA_TOOL_OPTIONS`, while `_JAVA_OPTIONS` can override command-line
|
||||
settings. Evidence reports the worker's observed `Runtime.maxMemory()` rounded
|
||||
down to MiB and retains exact bytes, Java version and visible processors in its
|
||||
raw runtime response. The observed allocatable heap can differ from configured
|
||||
`-Xmx`, especially with Serial GC. Heap is not a total RSS cap: the JVM also
|
||||
uses native memory. Independent REA instances have independent queues. Whole
|
||||
operations have a **120-second** execution deadline after reaching the queue's
|
||||
front; upstream decompilation is limited to **90 seconds**.
|
||||
|
||||
Decoded manifest and method text have a **1 MiB** upstream byte budget and report
|
||||
truncation. Class-name pages use indexed slices of up to 4,096 names without changing the
|
||||
complete, case-sensitive search contract. A protocol frame above **8 MiB**, or combined stdout/stderr above
|
||||
**32 MiB per operation**, fails with a diagnostic instead of returning an incomplete inventory
|
||||
as complete. These are memory-safety boundaries, not undocumented result caps.
|
||||
|
||||
## Real verification
|
||||
|
||||
Download the explicit public fixtures once; this installs no tools:
|
||||
|
||||
```sh
|
||||
npm run fixtures:android
|
||||
export REA_JADX_MCP_JAR="$PWD/_reference/apk-integration/jadx-headless-mcp-0.7.1-all.jar"
|
||||
export REA_ANDROID_TEST_APK="$PWD/_reference/apk-integration/ApiDemos-debug.apk"
|
||||
npm run verify:android
|
||||
```
|
||||
|
||||
The download script verifies fixed SHA-256 values, reuses matching files and
|
||||
refuses to overwrite a different existing file. APK/JAR files live under ignored
|
||||
`_reference/` and are excluded from package contents. The lane exercises actual
|
||||
REA CLI and MCP results with the real engine, separately from synthetic protocol
|
||||
regressions. It needs no Gradle build, emulator, Android SDK or Ghidra.
|
||||
@@ -0,0 +1 @@
|
||||
TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
|
||||
@@ -0,0 +1 @@
|
||||
window.hierarchyData = "eJydlctu2zAQRf9l1owSiXpQ2jVGgC6K1mjSVWAYjDS2iVCkQNIFikD/XihuYslMqsdGAAXeOXcuXy9gtHYWiscoSgnNSBKyDQGDO4mlE1pZKF6AZt1X8RqhgK+6adDcGaMNEHgWqoIijBiBo5FQQCm5tWivK11zobbYzbNBTxQcXC2BnCZCAc5WV12Vq9MPAuVByMqgguIxTkgSkiQhKSVZSLKMsJCwlOQxydmmJRAnnrMHUaM+ukUG+9oRny2BJPTgK65KlBKrRfiheoIBv/u10U6XWi7iD8Tj+JR6+J9Ya4eL4D3pODrzo18bXaK1Sxt/106A+8fhl+K/uZD8SS5r/lI/boL5Cdw7bpbt+7NyAjjtgVda7cT+aHh3V8xE+9pxeB734N/1rVDc/PnR4Fy2J52AZj30Sf3AzR7nJu5Jx9FR1M/81ohqj9/4UZUHPIOjJH0DC+XQ7HiJ9vrwur7boSa4GH5m4L3O8FaOaN7dvRHNvS34pWmkKF9X1DP4QTIfu/u01HhSNLvxVmklBSo3khM/07Yn2T1aK7QK+kUmJNWZYLFn4l+1/4UxZuFtNOFdYv6rqLVcazMnhqEwuBjO3DOUxYRmN5u2bf8CwZ/ljg=="
|
||||
@@ -0,0 +1,22 @@
|
||||
:root {
|
||||
--light-code-background: #FFFFFF;
|
||||
--dark-code-background: #1E1E1E;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) { :root {
|
||||
--code-background: var(--light-code-background);
|
||||
} }
|
||||
|
||||
@media (prefers-color-scheme: dark) { :root {
|
||||
--code-background: var(--dark-code-background);
|
||||
} }
|
||||
|
||||
:root[data-theme='light'] {
|
||||
--code-background: var(--light-code-background);
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] {
|
||||
--code-background: var(--dark-code-background);
|
||||
}
|
||||
|
||||
pre, code, math[display='block'] { background: var(--code-background); }
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 14 KiB |
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
window.navigationData = "eJylWktz2zYQ/i86e9rGrd3WN5mWR24dKSOp7SGT0UDgSkJMEhwAdKJ28t87JCgRAPF0jnG+BxZYYMGFPv43EfBVTO4mqK4LgpEgtJpcTfCRFDmDanL38YK4JxVipzVwLjE1EsfJ3aSkeVMA/1ER2GrQH46iLCZXkxdS5ZO76yRxXCDOveIeq3fXv327MhyygkAlBgNSCWB7hCM8JNWI5ubWYfGIsKDsNDiJUx1t0pMNr59+//XdzfW3T4rjA2GAxbRCxYkT7l8UHRu1KqypXA77psKtrNdjxNddb3/Rg6Ft1IEgOkzU4CU0OwJ+Ca53L6tQfOssYXPKw4mkCbcMn247XcYcOKZZql7wo2kdNPmJCyhtI/ZKmzTvws2qI6ow5BtKi0ASatCoZXSI204GXdxj1Z4MagBHWsKOwRf/2M+oqGHPl+9n96vZP9ts+f79dPGwHrRfESNo51QfMXW/n6+VgdeM7mA+Gr19aS8OGsu7snNa18DaGfxAmfBPj46NmySHvGMzGQ5ew5tbLRDWVIKU4I+gB0UNHTNAAhxlyz7/Z3kL1bsIaxBN7R95B4kad4eMrIJSVWH4Dq8OFnUmKrKhE7EDrYA3RZKsZPiEZTwZrfbk0LBOwLQZF2zp4KTay/XgiXsK/MFpZS6BPWWko4XoOfFZUxkZ49M+o4MlxLK+Pl2DZElwNcVxQcYJjguSsBUzVWIYWStyAXi3mZxlyzC6v0eNZFrXmaGiJGsvdAH58rNGjIOppUQlBRRUKDTBEBbccbWHvlQ+VXUjLBX8wt/qyOQCvkDqGSx3mFPcJIb2l0Zf4yOUiNuqrtPQJuApvmCMTzLe5KhLjDzVtRSUFpmyns6l0oBRK7VRGK4MtoqrRF9Wt7g/2787U2Cs2+JDKz9bzKeLbPaw3SyXz9tsudisptnGeuVyWTkkPMu/fHx8yp6mz99h6pDwmK5n6/XTcvEdnnYFj+XbrQIWegnIaYmIq/ew6y5LG8QOYLmFSupWBSU0HUxVJd9twvduF0szYMrwkQjAomGjg8+trrJCud/VAHskQ72wWY2InitAh519BdyIdsnngHKwfqU6jUyyt14BY5RZDrdeXf538hLPWtr4C1IXHRF8PSXtEhglP2b49OUXTtaWiKKAPMrBxgl7JEhHKn5gFAPnCcIqI0pfUEyLNIOBEnZYQUkFJOgrhLD6WiAWl5ImPqy9ISXQJkVdZYT1/6rQKyJFu5sTPEyWz2dB5UZc1hC3sUaEUMenHdHfqGjAfc6ooLj7f54z4Bxy/Z47riiassbyfw0fSqjEuilLpLaWA/o6LfjV8UBx0+KtzV6bvs4KVZFnwkVGG/u3r1P+wgrJt3OYNvKOEZLtzqa8YYnaAy1ksIICCcjPuZBmY5JDZn1GpJmcSd7KzYwmirGjmK1RYt9L9naMrhPXdwHmu6r0UsB8XXT6ElagL96Z4adyRwv3U00vpsPi+h9IwIEy8i+sv5C92LQT5Rmu4WBje2YiJxzTV2DL3WecyZM43stCjrQ6l+43ml3oEXbdNLw5NJUdaHrJLeZ6bWUkP8Azaip8BMujmCRvdVhC0306dOzGJucSazdxCnifXxUNa+Wye6n/9H5tOaYryWA0gRaLUa8/0sH+GK4ZOOd1WXeZl+AZkvI/lvSfEEaH2sg8FZSQd6aqkWqaqtvC+pnU4SLmyunhnJzR3R3YK7Cnak8TfQZi2OSBoENFuSDYLIJu/YET/u1A3Z+JzhU+AxJWdwW8phUftTpMRR0dKuDts0d3EwnJXoAhxc9noLtna2obFN+jrLySydieSQW2KmLKj0i+F54eZl6R3Oo6I1CX+j6ckRTtX2N/NGIbUsdnjf+pk3f7w1ES5TPO+gwxxiepWxWU8Hpkqg7jtum6TUbX4QPhAljgFxK9hxUdN+d+n1E0dqcIf0d8y/2eYIKKuPg0dFJ8Dh9nfLpThL8jvr56x4WngpOis7s4g9N8wuZGaG2r3jxAjIAGSFQYgmao6CbVfTCNlU2W9XD69D+KgXkm"
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,158 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>BinarySession | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/application_BinarySession.html">application/BinarySession</a></li>
|
||||
<li><a href="" aria-current="page">BinarySession</a></li></ul>
|
||||
<h1>Class BinarySession</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Owns the single active target shared by CLI and MCP adapters.</p>
|
||||
<p>Target transitions are serialized because each client dispatches Hopper API
|
||||
work on its dedicated Python thread, and switching targets tears that bridge
|
||||
down. A failed switch recreates the previous target instead of retaining a
|
||||
client whose bridge was already shut down.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel">
|
||||
<h4>Implements</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li><a href="../interfaces/application_HopperToolPort.HopperToolPort.html" class="tsd-signature-type tsd-kind-interface">HopperToolPort</a></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L27">src/application/BinarySession.ts:27</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#createclient" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>create<wbr/>Client</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="methods">Methods<a href="#methods" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#calltool" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>call<wbr/>Tool</span></a>
|
||||
<a href="#close" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>close</span></a>
|
||||
<a href="#open" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>open</span></a>
|
||||
<a href="#status" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>status</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorbinarysession"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">BinarySession</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">createClient</span><span class="tsd-signature-symbol">:</span> <a href="../types/application_BinarySession.BinaryClientFactory.html" class="tsd-signature-type tsd-kind-type-alias">BinaryClientFactory</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">BinarySession</a><a href="#constructorbinarysession" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">createClient</span>: <a href="../types/application_BinarySession.BinaryClientFactory.html" class="tsd-signature-type tsd-kind-type-alias">BinaryClientFactory</a></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">BinarySession</a></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L33">src/application/BinarySession.ts:33</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="createclient"><code class="tsd-tag">Readonly</code><span>create<wbr/>Client</span><a href="#createclient" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">createClient</span><span class="tsd-signature-symbol">:</span> <a href="../types/application_BinarySession.BinaryClientFactory.html" class="tsd-signature-type tsd-kind-type-alias">BinaryClientFactory</a></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L33">src/application/BinarySession.ts:33</a></li></ul></aside></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="methods-1">Methods<a href="#methods-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="calltool"><span>call<wbr/>Tool</span><a href="#calltool" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="calltool-1"><span class="tsd-kind-call-signature">callTool</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">name</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">arguments_</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <a href="../types/hopper_protocol.JsonValue.html" class="tsd-signature-type tsd-kind-type-alias">JsonValue</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">JSONType</span><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><a href="#calltool-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Invoke a Hopper tool against the active target.
|
||||
Calls may overlap, but a pending target transition prevents new calls from
|
||||
entering until the transition has settled.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">name</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">arguments_</span>: <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <a href="../types/hopper_protocol.JsonValue.html" class="tsd-signature-type tsd-kind-type-alias">JsonValue</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span> <span class="tsd-signature-symbol">}</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">JSONType</span><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<p>Implementation of <a href="../interfaces/application_HopperToolPort.HopperToolPort.html">HopperToolPort</a>.<a href="../interfaces/application_HopperToolPort.HopperToolPort.html#calltool">callTool</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L108">src/application/BinarySession.ts:108</a></li></ul></aside></div></li></ul></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="close"><span>close</span><a href="#close" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="close-1"><span class="tsd-kind-call-signature">close</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">null</span><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><a href="#close-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Close the active target, if any.</p>
|
||||
</div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">null</span><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L80">src/application/BinarySession.ts:80</a></li></ul></aside></div></li></ul></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="open"><span>open</span><a href="#open" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="open-1"><span class="tsd-kind-call-signature">open</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">path</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span><span class="tsd-signature-symbol">;</span> <span class="tsd-kind-property">targetKind</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">"executable"</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"database"</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><a href="../interfaces/domain_binaryTarget.BinaryTarget.html" class="tsd-signature-type tsd-kind-interface">BinaryTarget</a><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><a href="#open-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Open or switch targets after draining calls against the current target.
|
||||
Returns the switch failure even if best-effort restoration also fails.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">path</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span><span class="tsd-signature-symbol">;</span> <span class="tsd-kind-property">targetKind</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">"executable"</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"database"</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol"> = {}</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><a href="../interfaces/domain_binaryTarget.BinaryTarget.html" class="tsd-signature-type tsd-kind-interface">BinaryTarget</a><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L39">src/application/BinarySession.ts:39</a></li></ul></aside></div></li></ul></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="status"><span>status</span><a href="#status" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="status-1"><span class="tsd-kind-call-signature">status</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">JSONType</span><a href="#status-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Describe the active binary session.</p>
|
||||
</div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">JSONType</span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/BinarySession.ts#L91">src/application/BinarySession.ts:91</a></li></ul></aside></div></li></ul></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#createclient"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>create<wbr/>Client</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Methods</summary>
|
||||
<div><a href="#calltool"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>call<wbr/>Tool</span></a><a href="#close"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>close</span></a><a href="#open"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>open</span></a><a href="#status"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>status</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>EnhancedTools | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/application_EnhancedTools.html">application/EnhancedTools</a></li>
|
||||
<li><a href="" aria-current="page">EnhancedTools</a></li></ul>
|
||||
<h1>Class EnhancedTools</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Composes official Hopper operations into bounded reverse-engineering tools.
|
||||
Inputs are parsed again at this application boundary even though MCP normally
|
||||
validates them, allowing direct callers to use the same service safely.</p>
|
||||
</div></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/EnhancedTools.ts#L29">src/application/EnhancedTools.ts:29</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="methods">Methods<a href="#methods" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#execute" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>execute</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorenhancedtools"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">EnhancedTools</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">hopper</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/application_HopperToolPort.HopperToolPort.html" class="tsd-signature-type tsd-kind-interface">HopperToolPort</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">EnhancedTools</a><a href="#constructorenhancedtools" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">hopper</span>: <a href="../interfaces/application_HopperToolPort.HopperToolPort.html" class="tsd-signature-type tsd-kind-interface">HopperToolPort</a></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">EnhancedTools</a></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/EnhancedTools.ts#L30">src/application/EnhancedTools.ts:30</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="methods-1">Methods<a href="#methods-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="execute"><span>execute</span><a href="#execute" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="execute-1"><span class="tsd-kind-call-signature">execute</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">name</span><span class="tsd-signature-symbol">:</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"binary_overview"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"swift_classes"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"get_objc_classes"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"get_objc_protocols"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"batch_decompile"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"get_call_graph"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"analyze_swift_types"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"find_xrefs_to_name"</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">input</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">unknown</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">EnhancedResult</span><a href="#execute-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Parse inputs again at the direct-call boundary, then dispatch exhaustively.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">name</span>: <br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"binary_overview"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"swift_classes"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"get_objc_classes"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"get_objc_protocols"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"batch_decompile"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"get_call_graph"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"analyze_swift_types"</span><br/> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"find_xrefs_to_name"</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">input</span>: <span class="tsd-signature-type">unknown</span></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">signal</span>: <span class="tsd-signature-type">AbortSignal</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">EnhancedResult</span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/EnhancedTools.ts#L33">src/application/EnhancedTools.ts:33</a></li></ul></aside></div></li></ul></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Methods</summary>
|
||||
<div><a href="#execute"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>execute</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>BinaryTargetError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">BinaryTargetError</a></li></ul>
|
||||
<h1>Class BinaryTargetError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>A supplied target path could not be safely opened as a supported binary.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="98">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.BinaryTargetError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">BinaryTargetError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L86">src/domain/errors.ts:86</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
<a href="#path" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>path</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorbinarytargeterror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">BinaryTargetError</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">path</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">reason</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">ErrorOptions</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">BinaryTargetError</a><a href="#constructorbinarytargeterror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">path</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">reason</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-type">ErrorOptions</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">BinaryTargetError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L88">src/domain/errors.ts:88</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"BinaryTargetError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L87">src/domain/errors.ts:87</a></li></ul></aside></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="path"><code class="tsd-tag">Readonly</code><span>path</span><a href="#path" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">path</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L89">src/domain/errors.ts:89</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a><a href="#path"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>path</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>ConfigurationError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">ConfigurationError</a></li></ul>
|
||||
<h1>Class ConfigurationError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Runtime configuration could not be parsed safely.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="86">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.ConfigurationError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">ConfigurationError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L71">src/domain/errors.ts:71</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link tsd-is-external"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member tsd-is-external">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures tsd-is-external">
|
||||
<li class="tsd-is-external">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorconfigurationerror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">ConfigurationError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">message</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">ConfigurationError</a><a href="#constructorconfigurationerror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">message</span>: <span class="tsd-signature-type">string</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">ConfigurationError</a></h4><aside class="tsd-sources">
|
||||
<p>Inherited from <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in node_modules/typescript/lib/lib.es5.d.ts:1082</li></ul></aside></div></li>
|
||||
<li class="tsd-is-external">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorconfigurationerror-1"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">ConfigurationError</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">message</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">ErrorOptions</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">ConfigurationError</a><a href="#constructorconfigurationerror-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">message</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-type">ErrorOptions</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">ConfigurationError</a></h4><aside class="tsd-sources">
|
||||
<p>Inherited from <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in node_modules/typescript/lib/lib.es5.d.ts:1082</li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"ConfigurationError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L72">src/domain/errors.ts:72</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor" class="tsd-is-external"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperCancelledError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperCancelledError</a></li></ul>
|
||||
<h1>Class HopperCancelledError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>The caller cancelled a Hopper operation before it completed.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="51">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperCancelledError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperCancelledError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L16">src/domain/errors.ts:16</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhoppercancellederror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperCancelledError</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperCancelledError</a><a href="#constructorhoppercancellederror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperCancelledError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L19">src/domain/errors.ts:19</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperCancelledError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L17">src/domain/errors.ts:17</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperError</a></li></ul>
|
||||
<h1>Class HopperError<code class="tsd-tag">Abstract</code></h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Base class for expected Hopper integration failures.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="37">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-signature-type">Error</span>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperError</span>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperTimeoutError.html" class="tsd-signature-type tsd-kind-class">HopperTimeoutError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperCancelledError.html" class="tsd-signature-type tsd-kind-class">HopperCancelledError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperProtocolError.html" class="tsd-signature-type tsd-kind-class">HopperProtocolError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperRemoteError.html" class="tsd-signature-type tsd-kind-class">HopperRemoteError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperProcessError.html" class="tsd-signature-type tsd-kind-class">HopperProcessError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperUnavailableError.html" class="tsd-signature-type tsd-kind-class">HopperUnavailableError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperStartError.html" class="tsd-signature-type tsd-kind-class">HopperStartError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.ConfigurationError.html" class="tsd-signature-type tsd-kind-class">ConfigurationError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.NoBinaryOpenError.html" class="tsd-signature-type tsd-kind-class">NoBinaryOpenError</a></li>
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.BinaryTargetError.html" class="tsd-signature-type tsd-kind-class">BinaryTargetError</a></li></ul></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L2">src/domain/errors.ts:2</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link tsd-is-external"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member tsd-is-external">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures tsd-is-external">
|
||||
<li class="tsd-is-external">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhoppererror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">message</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperError</a><a href="#constructorhoppererror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">message</span>: <span class="tsd-signature-type">string</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperError</a></h4><aside class="tsd-sources">
|
||||
<p>Inherited from Error.constructor</p>
|
||||
<ul>
|
||||
<li>Defined in node_modules/typescript/lib/lib.es5.d.ts:1082</li></ul></aside></div></li>
|
||||
<li class="tsd-is-external">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhoppererror-1"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">message</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">ErrorOptions</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperError</a><a href="#constructorhoppererror-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">message</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-type">ErrorOptions</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperError</a></h4><aside class="tsd-sources">
|
||||
<p>Inherited from Error.constructor</p>
|
||||
<ul>
|
||||
<li>Defined in node_modules/typescript/lib/lib.es5.d.ts:1082</li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Abstract</code> <code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L3">src/domain/errors.ts:3</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor" class="tsd-is-external"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperProcessError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperProcessError</a></li></ul>
|
||||
<h1>Class HopperProcessError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>The owned Hopper bridge stopped before its client was closed.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="71">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperProcessError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperProcessError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L42">src/domain/errors.ts:42</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
<a href="#exitcode" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>exit<wbr/>Code</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperprocesserror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperProcessError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">exitCode</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">null</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperProcessError</a><a href="#constructorhopperprocesserror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">exitCode</span>: <span class="tsd-signature-type">number</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">null</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperProcessError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L45">src/domain/errors.ts:45</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperProcessError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L43">src/domain/errors.ts:43</a></li></ul></aside></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="exitcode"><code class="tsd-tag">Readonly</code><span>exit<wbr/>Code</span><a href="#exitcode" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">exitCode</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">null</span></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L45">src/domain/errors.ts:45</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a><a href="#exitcode"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>exit<wbr/>Code</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperProtocolError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperProtocolError</a></li></ul>
|
||||
<h1>Class HopperProtocolError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Hopper returned bytes or JSON that do not satisfy the NDJSON RPC contract.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="55">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperProtocolError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperProtocolError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L25">src/domain/errors.ts:25</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link tsd-is-external"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member tsd-is-external">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures tsd-is-external">
|
||||
<li class="tsd-is-external">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperprotocolerror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperProtocolError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">message</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperProtocolError</a><a href="#constructorhopperprotocolerror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">message</span>: <span class="tsd-signature-type">string</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperProtocolError</a></h4><aside class="tsd-sources">
|
||||
<p>Inherited from <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in node_modules/typescript/lib/lib.es5.d.ts:1082</li></ul></aside></div></li>
|
||||
<li class="tsd-is-external">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperprotocolerror-1"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperProtocolError</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">message</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">ErrorOptions</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperProtocolError</a><a href="#constructorhopperprotocolerror-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">message</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-type">ErrorOptions</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperProtocolError</a></h4><aside class="tsd-sources">
|
||||
<p>Inherited from <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in node_modules/typescript/lib/lib.es5.d.ts:1082</li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperProtocolError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L26">src/domain/errors.ts:26</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor" class="tsd-is-external"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperRemoteError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperRemoteError</a></li></ul>
|
||||
<h1>Class HopperRemoteError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Hopper's JSON-RPC endpoint returned an expected remote error response.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="63">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperRemoteError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperRemoteError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L30">src/domain/errors.ts:30</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
<a href="#code" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>code</span></a>
|
||||
<a href="#safemessage" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>safe<wbr/>Message</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperremoteerror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperRemoteError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">code</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span><span class="tsd-signature-symbol">,</span> <span class="tsd-kind-parameter">safeMessage</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperRemoteError</a><a href="#constructorhopperremoteerror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">code</span>: <span class="tsd-signature-type">number</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">safeMessage</span>: <span class="tsd-signature-type">string</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperRemoteError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L33">src/domain/errors.ts:33</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperRemoteError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L31">src/domain/errors.ts:31</a></li></ul></aside></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="code"><code class="tsd-tag">Readonly</code><span>code</span><a href="#code" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">code</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L34">src/domain/errors.ts:34</a></li></ul></aside></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="safemessage"><code class="tsd-tag">Readonly</code><span>safe<wbr/>Message</span><a href="#safemessage" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">safeMessage</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L35">src/domain/errors.ts:35</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a><a href="#code"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>code</span></a><a href="#safemessage"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>safe<wbr/>Message</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperStartError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperStartError</a></li></ul>
|
||||
<h1>Class HopperStartError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Hopper or the repository bridge could not be started.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="81">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperStartError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperStartError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L62">src/domain/errors.ts:62</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperstarterror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperStartError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">ErrorOptions</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperStartError</a><a href="#constructorhopperstarterror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-type">ErrorOptions</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperStartError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L65">src/domain/errors.ts:65</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperStartError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L63">src/domain/errors.ts:63</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperTimeoutError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperTimeoutError</a></li></ul>
|
||||
<h1>Class HopperTimeoutError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Hopper did not respond within the configured operation deadline.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="45">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperTimeoutError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperTimeoutError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L7">src/domain/errors.ts:7</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
<a href="#timeoutms" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>timeout<wbr/>Ms</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhoppertimeouterror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperTimeoutError</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">timeoutMs</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperTimeoutError</a><a href="#constructorhoppertimeouterror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">timeoutMs</span>: <span class="tsd-signature-type">number</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperTimeoutError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L10">src/domain/errors.ts:10</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperTimeoutError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L8">src/domain/errors.ts:8</a></li></ul></aside></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="timeoutms"><code class="tsd-tag">Readonly</code><span>timeout<wbr/>Ms</span><a href="#timeoutms" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">timeoutMs</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L10">src/domain/errors.ts:10</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a><a href="#timeoutms"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>timeout<wbr/>Ms</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperUnavailableError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">HopperUnavailableError</a></li></ul>
|
||||
<h1>Class HopperUnavailableError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Hopper did not make the repository bridge available after launch.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="77">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.HopperUnavailableError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">HopperUnavailableError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L51">src/domain/errors.ts:51</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperunavailableerror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperUnavailableError</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperUnavailableError</a><a href="#constructorhopperunavailableerror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperUnavailableError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L54">src/domain/errors.ts:54</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"HopperUnavailableError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L52">src/domain/errors.ts:52</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>NoBinaryOpenError | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/domain_errors.html">domain/errors</a></li>
|
||||
<li><a href="" aria-current="page">NoBinaryOpenError</a></li></ul>
|
||||
<h1>Class NoBinaryOpenError</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>No binary session exists for an analysis request.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel tsd-hierarchy" data-refl="94">
|
||||
<h4>Hierarchy (<a href="../hierarchy.html#domain/errors.NoBinaryOpenError">View Summary</a>)</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li class="tsd-hierarchy-item"><span class="tsd-hierarchy-target">NoBinaryOpenError</span></li></ul></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L76">src/domain/errors.ts:76</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#_tag" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructornobinaryopenerror"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">NoBinaryOpenError</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">NoBinaryOpenError</a><a href="#constructornobinaryopenerror" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">NoBinaryOpenError</a></h4><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#constructor">constructor</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L78">src/domain/errors.ts:78</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="_tag"><code class="tsd-tag">Readonly</code><span>_<wbr/>tag</span><a href="#_tag" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">_tag</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"NoBinaryOpenError"</span></div><aside class="tsd-sources">
|
||||
<p>Overrides <a href="domain_errors.HopperError.html">HopperError</a>.<a href="domain_errors.HopperError.html#_tag">_tag</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/domain/errors.ts#L77">src/domain/errors.ts:77</a></li></ul></aside></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#_tag"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>_<wbr/>tag</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperApplicationLauncher | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/hopper_BridgeLauncher.html">hopper/BridgeLauncher</a></li>
|
||||
<li><a href="" aria-current="page">HopperApplicationLauncher</a></li></ul>
|
||||
<h1>Class HopperApplicationLauncher</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Launches Hopper through its documented CLI and injects only REA's owned bridge.</p>
|
||||
<p>Hopper's <code>hopper</code> helper internally issues an AppleScript <code>activate</code> command.
|
||||
Consequently, opening a target may bring Hopper to the foreground even though
|
||||
REA first asks macOS to start the application hidden and in the background.
|
||||
REA cannot reliably suppress that activation without replacing Hopper's
|
||||
supported launcher; callers must treat possible foreground UI as an upstream
|
||||
Hopper constraint, not as evidence that analysis failed.</p>
|
||||
<p>The returned process is the short-lived launcher helper, not the Hopper app,
|
||||
so REA does not claim ownership of the application's lifetime.</p>
|
||||
</div></section>
|
||||
<section class="tsd-panel">
|
||||
<h4>Implements</h4>
|
||||
<ul class="tsd-hierarchy">
|
||||
<li><a href="../interfaces/hopper_BridgeLauncher.BridgeLauncher.html" class="tsd-signature-type tsd-kind-interface">BridgeLauncher</a></li></ul></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/BridgeLauncher.ts#L55">src/hopper/BridgeLauncher.ts:55</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="properties">Properties<a href="#properties" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#options" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>options</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="methods">Methods<a href="#methods" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#launch" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>launch</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperapplicationlauncher"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperApplicationLauncher</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/hopper_BridgeLauncher.HopperApplicationLauncherOptions.html" class="tsd-signature-type tsd-kind-interface">HopperApplicationLauncherOptions</a><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperApplicationLauncher</a><a href="#constructorhopperapplicationlauncher" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">options</span>: <a href="../interfaces/hopper_BridgeLauncher.HopperApplicationLauncherOptions.html" class="tsd-signature-type tsd-kind-interface">HopperApplicationLauncherOptions</a></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperApplicationLauncher</a></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/BridgeLauncher.ts#L56">src/hopper/BridgeLauncher.ts:56</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="properties-1">Properties<a href="#properties-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="options"><code class="tsd-tag">Readonly</code><span>options</span><a href="#options" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-signature"><span class="tsd-kind-property">options</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/hopper_BridgeLauncher.HopperApplicationLauncherOptions.html" class="tsd-signature-type tsd-kind-interface">HopperApplicationLauncherOptions</a></div><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/BridgeLauncher.ts#L56">src/hopper/BridgeLauncher.ts:56</a></li></ul></aside></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="methods-1">Methods<a href="#methods-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="launch"><span>launch</span><a href="#launch" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="launch-1"><span class="tsd-kind-call-signature">launch</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">session</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/hopper_BridgeLauncher.BridgeSession.html" class="tsd-signature-type tsd-kind-interface">BridgeSession</a><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><a href="../interfaces/hopper_BridgeLauncher.BridgeLaunch.html" class="tsd-signature-type tsd-kind-interface">BridgeLaunch</a><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperCancelledError.html" class="tsd-signature-type tsd-kind-class">HopperCancelledError</a> <span class="tsd-signature-symbol">|</span> <a href="domain_errors.HopperStartError.html" class="tsd-signature-type tsd-kind-class">HopperStartError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><a href="#launch-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">session</span>: <a href="../interfaces/hopper_BridgeLauncher.BridgeSession.html" class="tsd-signature-type tsd-kind-interface">BridgeSession</a></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol"> = {}</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><a href="../interfaces/hopper_BridgeLauncher.BridgeLaunch.html" class="tsd-signature-type tsd-kind-interface">BridgeLaunch</a><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperCancelledError.html" class="tsd-signature-type tsd-kind-class">HopperCancelledError</a> <span class="tsd-signature-symbol">|</span> <a href="domain_errors.HopperStartError.html" class="tsd-signature-type tsd-kind-class">HopperStartError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<p>Implementation of <a href="../interfaces/hopper_BridgeLauncher.BridgeLauncher.html">BridgeLauncher</a>.<a href="../interfaces/hopper_BridgeLauncher.BridgeLauncher.html#launch">launch</a></p>
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/BridgeLauncher.ts#L58">src/hopper/BridgeLauncher.ts:58</a></li></ul></aside></div></li></ul></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Properties"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Properties</summary>
|
||||
<div><a href="#options"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Property"><use href="../assets/icons.svg#icon-1024"></use></svg><span>options</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Methods</summary>
|
||||
<div><a href="#launch"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>launch</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>HopperClient | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/hopper_HopperClient.html">hopper/HopperClient</a></li>
|
||||
<li><a href="" aria-current="page">HopperClient</a></li></ul>
|
||||
<h1>Class HopperClient</h1></div>
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography"><p>Owns one authenticated NDJSON-over-Unix-socket bridge session.</p>
|
||||
<p>Each instance creates a private directory and random bearer token, correlates
|
||||
concurrent requests by numeric id, and removes its artifacts on close. It
|
||||
only terminates launch processes explicitly marked as owned; the normal
|
||||
Hopper launcher does not confer ownership of the GUI application.</p>
|
||||
</div></section><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/HopperClient.ts#L59">src/hopper/HopperClient.ts:59</a></li></ul></aside>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<details class="tsd-index-content tsd-accordion" open><summary class="tsd-accordion-summary tsd-index-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h5 class="tsd-index-heading uppercase">Index</h5></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="constructors">Constructors<a href="#constructors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#constructor" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a>
|
||||
</div></section>
|
||||
<section class="tsd-index-section">
|
||||
<h3 class="tsd-index-heading tsd-anchor-link" id="methods">Methods<a href="#methods" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<div class="tsd-index-list"><a href="#calltool" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>call<wbr/>Tool</span></a>
|
||||
<a href="#close" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>close</span></a>
|
||||
<a href="#start" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>start</span></a>
|
||||
</div></section></div></details></section></section>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="constructors-1">Constructors<a href="#constructors-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="constructor"><span>constructor</span><a href="#constructor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="constructorhopperclient"><span class="tsd-signature-keyword">new</span> <span class="tsd-kind-constructor-signature">HopperClient</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/hopper_HopperClient.HopperClientOptions.html" class="tsd-signature-type tsd-kind-interface">HopperClientOptions</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <a href="" class="tsd-signature-type tsd-kind-class">HopperClient</a><a href="#constructorhopperclient" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">options</span>: <a href="../interfaces/hopper_HopperClient.HopperClientOptions.html" class="tsd-signature-type tsd-kind-interface">HopperClientOptions</a></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="" class="tsd-signature-type tsd-kind-class">HopperClient</a></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/HopperClient.ts#L75">src/hopper/HopperClient.ts:75</a></li></ul></aside></div></li></ul></section></section></details>
|
||||
<details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h2 class="tsd-anchor-link" id="methods-1">Methods<a href="#methods-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2></summary>
|
||||
<section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="calltool"><span>call<wbr/>Tool</span><a href="#calltool" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="calltool-1"><span class="tsd-kind-call-signature">callTool</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">name</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">arguments_</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <a href="../types/hopper_protocol.JsonValue.html" class="tsd-signature-type tsd-kind-type-alias">JsonValue</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">options</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span><span class="tsd-signature-symbol">;</span> <span class="tsd-kind-property">timeoutMs</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">number</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">JSONType</span><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><a href="#calltool-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Invoke one declared operation, returning timeout and cancellation as values.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">name</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">arguments_</span>: <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <a href="../types/hopper_protocol.JsonValue.html" class="tsd-signature-type tsd-kind-type-alias">JsonValue</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol"> = {}</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">options</span>: <span class="tsd-signature-symbol">{</span> <span class="tsd-kind-property">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span><span class="tsd-signature-symbol">;</span> <span class="tsd-kind-property">timeoutMs</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">number</span> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol"> = {}</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">JSONType</span><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/HopperClient.ts#L140">src/hopper/HopperClient.ts:140</a></li></ul></aside></div></li></ul></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="close"><span>close</span><a href="#close" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="close-1"><span class="tsd-kind-call-signature">close</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">void</span><span class="tsd-signature-symbol">></span><a href="#close-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Stop the bridge, settle outstanding requests, and remove session artifacts.</p>
|
||||
</div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">void</span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/HopperClient.ts#L154">src/hopper/HopperClient.ts:154</a></li></ul></aside></div></li></ul></section>
|
||||
<section class="tsd-panel tsd-member">
|
||||
<h3 class="tsd-anchor-link" id="start"><span>start</span><a href="#start" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="start-1"><span class="tsd-kind-call-signature">start</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">signal</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">AbortSignal</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><a href="../interfaces/hopper_HopperClient.HopperServerInfo.html" class="tsd-signature-type tsd-kind-interface">HopperServerInfo</a><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><a href="#start-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Launch the bridge once and complete its authenticated health handshake.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">signal</span>: <span class="tsd-signature-type">AbortSignal</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/domain_result.Result.html" class="tsd-signature-type tsd-kind-type-alias">Result</a><span class="tsd-signature-symbol"><</span><a href="../interfaces/hopper_HopperClient.HopperServerInfo.html" class="tsd-signature-type tsd-kind-interface">HopperServerInfo</a><span class="tsd-signature-symbol">,</span> <a href="domain_errors.HopperError.html" class="tsd-signature-type tsd-kind-class">HopperError</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/hopper/HopperClient.ts#L84">src/hopper/HopperClient.ts:84</a></li></ul></aside></div></li></ul></section></section></details></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div>
|
||||
<details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>On This Page</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Constructors"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Constructors</summary>
|
||||
<div><a href="#constructor"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Constructor"><use href="../assets/icons.svg#icon-512"></use></svg><span>constructor</span></a></div></details>
|
||||
<details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Methods"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Methods</summary>
|
||||
<div><a href="#calltool"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>call<wbr/>Tool</span></a><a href="#close"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>close</span></a><a href="#start"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Method"><use href="../assets/icons.svg#icon-2048"></use></svg><span>start</span></a></div></details></div></details></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>runDirectAnalysis | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/application_DirectAnalysis.html">application/DirectAnalysis</a></li>
|
||||
<li><a href="" aria-current="page">runDirectAnalysis</a></li></ul>
|
||||
<h1>Function runDirectAnalysis</h1></div>
|
||||
<section class="tsd-panel">
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="rundirectanalysis"><span class="tsd-kind-call-signature">runDirectAnalysis</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">path</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">tool</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">"binary_overview"</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"procedure_pseudo_code"</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">arguments_</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <a href="../types/hopper_protocol.JsonValue.html" class="tsd-signature-type tsd-kind-type-alias">JsonValue</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">JSONType</span><span class="tsd-signature-symbol">></span><a href="#rundirectanalysis" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Open one binary, execute one tool, and always release the bridge session.
|
||||
Unlike MCP mode, every CLI invocation is intentionally isolated and does not
|
||||
retain a target or bridge resources for a subsequent command.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">path</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">tool</span>: <span class="tsd-signature-type">"binary_overview"</span> <span class="tsd-signature-symbol">|</span> <span class="tsd-signature-type">"procedure_pseudo_code"</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">arguments_</span>: <span class="tsd-signature-type">Readonly</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">Record</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span> <a href="../types/hopper_protocol.JsonValue.html" class="tsd-signature-type tsd-kind-type-alias">JsonValue</a><span class="tsd-signature-symbol">></span><span class="tsd-signature-symbol">></span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><span class="tsd-signature-type">JSONType</span><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/DirectAnalysis.ts#L10">src/application/DirectAnalysis.ts:10</a></li></ul></aside></div></li></ul></section></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>runDoctor | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/application_Doctor.html">application/Doctor</a></li>
|
||||
<li><a href="" aria-current="page">runDoctor</a></li></ul>
|
||||
<h1>Function runDoctor</h1></div>
|
||||
<section class="tsd-panel">
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="rundoctor"><span class="tsd-kind-call-signature">runDoctor</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">target</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">host</span><span class="tsd-signature-symbol">?:</span> <a href="../interfaces/application_Doctor.DoctorHost.html" class="tsd-signature-type tsd-kind-interface">DoctorHost</a><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><br/> <span class="tsd-signature-symbol">{</span><br/> <span class="tsd-kind-property">checks</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-keyword">readonly</span> <a href="../interfaces/application_Doctor.DoctorCheck.html" class="tsd-signature-type tsd-kind-interface">DoctorCheck</a><span class="tsd-signature-symbol">[]</span><span class="tsd-signature-symbol">;</span><br/> <span class="tsd-kind-property">healthy</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">boolean</span><span class="tsd-signature-symbol">;</span><br/> <span class="tsd-kind-property">hopperPath</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">;</span><br/> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">></span><a href="#rundoctor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Check requirements and an optional target without mutating the host.
|
||||
Every failed check includes remediation suitable for either the one-shot CLI
|
||||
or an agent consuming the structured result.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">target</span>: <span class="tsd-signature-type">string</span></span></li>
|
||||
<li><span><span class="tsd-kind-parameter">host</span>: <a href="../interfaces/application_Doctor.DoctorHost.html" class="tsd-signature-type tsd-kind-interface">DoctorHost</a><span class="tsd-signature-symbol"> = ...</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><br/> <span class="tsd-signature-symbol">{</span><br/> <span class="tsd-kind-property">checks</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-keyword">readonly</span> <a href="../interfaces/application_Doctor.DoctorCheck.html" class="tsd-signature-type tsd-kind-interface">DoctorCheck</a><span class="tsd-signature-symbol">[]</span><span class="tsd-signature-symbol">;</span><br/> <span class="tsd-kind-property">healthy</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">boolean</span><span class="tsd-signature-symbol">;</span><br/> <span class="tsd-kind-property">hopperPath</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">;</span><br/> <span class="tsd-signature-symbol">}</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/Doctor.ts#L39">src/application/Doctor.ts:39</a></li></ul></aside></div></li></ul></section></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>systemDoctorHost | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/application_Doctor.html">application/Doctor</a></li>
|
||||
<li><a href="" aria-current="page">systemDoctorHost</a></li></ul>
|
||||
<h1>Function systemDoctorHost</h1></div>
|
||||
<section class="tsd-panel">
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="systemdoctorhost"><span class="tsd-kind-call-signature">systemDoctorHost</span><span class="tsd-signature-symbol">()</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/application_Doctor.DoctorHost.html" class="tsd-signature-type tsd-kind-interface">DoctorHost</a><a href="#systemdoctorhost" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Create diagnostics backed by the current process and host commands.</p>
|
||||
</div>
|
||||
<h4 class="tsd-returns-title">Returns <a href="../interfaces/application_Doctor.DoctorHost.html" class="tsd-signature-type tsd-kind-interface">DoctorHost</a></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/Doctor.ts#L105">src/application/Doctor.ts:105</a></li></ul></aside></div></li></ul></section></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>configureJsonClient | rea-agents - v0.1.0</title><meta name="description" content="Documentation for rea-agents"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar">
|
||||
<div class="tsd-toolbar-contents container"><a href="../index.html" class="title">rea-agents - v0.1.0</a>
|
||||
<div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/>
|
||||
<ul role="listbox" id="tsd-search-results"></ul>
|
||||
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
|
||||
<div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header>
|
||||
<div class="container container-main">
|
||||
<div class="col-content">
|
||||
<div class="tsd-page-title">
|
||||
<ul class="tsd-breadcrumb" aria-label="Breadcrumb">
|
||||
<li><a href="../modules/application_Setup.html">application/Setup</a></li>
|
||||
<li><a href="" aria-current="page">configureJsonClient</a></li></ul>
|
||||
<h1>Function configureJsonClient</h1></div>
|
||||
<section class="tsd-panel">
|
||||
<ul class="tsd-signatures">
|
||||
<li class="">
|
||||
<div class="tsd-signature tsd-anchor-link" id="configurejsonclient"><span class="tsd-kind-call-signature">configureJsonClient</span><span class="tsd-signature-symbol">(</span><br/> <span class="tsd-kind-parameter">client</span><span class="tsd-signature-symbol">:</span> <a href="../interfaces/application_Setup.SetupClient.html" class="tsd-signature-type tsd-kind-interface">SetupClient</a><span class="tsd-signature-symbol">,</span><br/> <span class="tsd-kind-parameter">hopperPath</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span><span class="tsd-signature-symbol">,</span><br/><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/application_Setup.ClientConfigurationResult.html" class="tsd-signature-type tsd-kind-type-alias">ClientConfigurationResult</a><span class="tsd-signature-symbol">></span><a href="#configurejsonclient" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></div>
|
||||
<div class="tsd-description">
|
||||
<div class="tsd-comment tsd-typography"><p>Back up, atomically update, and semantically read back one JSON MCP configuration.</p>
|
||||
</div>
|
||||
<div class="tsd-parameters">
|
||||
<h4 class="tsd-parameters-title">Parameters</h4>
|
||||
<ul class="tsd-parameter-list">
|
||||
<li><span><span class="tsd-kind-parameter">client</span>: <a href="../interfaces/application_Setup.SetupClient.html" class="tsd-signature-type tsd-kind-interface">SetupClient</a></span></li>
|
||||
<li><span><code class="tsd-tag">Optional</code><span class="tsd-kind-parameter">hopperPath</span>: <span class="tsd-signature-type">string</span></span></li></ul></div>
|
||||
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol"><</span><a href="../types/application_Setup.ClientConfigurationResult.html" class="tsd-signature-type tsd-kind-type-alias">ClientConfigurationResult</a><span class="tsd-signature-symbol">></span></h4><aside class="tsd-sources">
|
||||
<ul>
|
||||
<li>Defined in <a href="https://github.com/morluto/rea/blob/main/src/application/Setup.ts#L198">src/application/Setup.ts:198</a></li></ul></aside></div></li></ul></section></div>
|
||||
<div class="col-sidebar">
|
||||
<div class="page-menu">
|
||||
<div class="tsd-navigation settings">
|
||||
<details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>
|
||||
<h3>Settings</h3></summary>
|
||||
<div class="tsd-accordion-details">
|
||||
<div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span>
|
||||
<ul id="tsd-filter-options">
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li>
|
||||
<li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div>
|
||||
<div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div>
|
||||
<div class="site-menu">
|
||||
<nav class="tsd-navigation"><a href="../index.html">rea-agents - v0.1.0</a>
|
||||
<ul class="tsd-small-nested-navigation" id="tsd-nav-container">
|
||||
<li>Loading...</li></ul></nav></div></div></div><footer>
|
||||
<p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer>
|
||||
<div class="overlay"></div></body></html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user