epi-recorder 2.1.3__py3-none-any.whl → 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epi_analyzer/__init__.py +9 -0
- epi_analyzer/detector.py +337 -0
- epi_cli/__init__.py +4 -0
- epi_cli/__main__.py +4 -0
- epi_cli/chat.py +21 -3
- epi_cli/debug.py +107 -0
- epi_cli/keys.py +4 -0
- epi_cli/ls.py +5 -1
- epi_cli/main.py +8 -0
- epi_cli/record.py +4 -0
- epi_cli/run.py +12 -4
- epi_cli/verify.py +4 -0
- epi_cli/view.py +4 -0
- epi_core/__init__.py +5 -1
- epi_core/container.py +68 -55
- epi_core/redactor.py +4 -0
- epi_core/schemas.py +6 -2
- epi_core/serialize.py +4 -0
- epi_core/storage.py +186 -0
- epi_core/trust.py +4 -0
- epi_recorder/__init__.py +13 -1
- epi_recorder/api.py +211 -5
- epi_recorder/async_api.py +151 -0
- epi_recorder/bootstrap.py +4 -0
- epi_recorder/environment.py +4 -0
- epi_recorder/patcher.py +79 -19
- epi_recorder/test_import.py +2 -0
- epi_recorder/test_script.py +2 -0
- epi_recorder/wrappers/__init__.py +16 -0
- epi_recorder/wrappers/base.py +79 -0
- epi_recorder/wrappers/openai.py +178 -0
- epi_recorder-2.3.0.dist-info/METADATA +269 -0
- epi_recorder-2.3.0.dist-info/RECORD +41 -0
- {epi_recorder-2.1.3.dist-info → epi_recorder-2.3.0.dist-info}/WHEEL +1 -1
- epi_recorder-2.3.0.dist-info/licenses/LICENSE +21 -0
- {epi_recorder-2.1.3.dist-info → epi_recorder-2.3.0.dist-info}/top_level.txt +1 -0
- epi_viewer_static/app.js +113 -7
- epi_viewer_static/crypto.js +3 -0
- epi_viewer_static/index.html +4 -2
- epi_viewer_static/viewer_lite.css +3 -1
- epi_postinstall.py +0 -197
- epi_recorder-2.1.3.dist-info/METADATA +0 -577
- epi_recorder-2.1.3.dist-info/RECORD +0 -34
- epi_recorder-2.1.3.dist-info/licenses/LICENSE +0 -201
- {epi_recorder-2.1.3.dist-info → epi_recorder-2.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: epi-recorder
|
|
3
|
+
Version: 2.3.0
|
|
4
|
+
Summary: Verifiable execution evidence for AI systems. Portable, cryptographically signed artifacts.
|
|
5
|
+
Author-email: EPI Labs <mohdibrahim@epilabs.org>
|
|
6
|
+
Maintainer-email: Mohd Ibrahim Afridi <mohdibrahim@epilabs.org>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://epilabs.org
|
|
9
|
+
Project-URL: Documentation, https://epilabs.org/docs
|
|
10
|
+
Project-URL: Repository, https://github.com/mohdibrahimaiml/epi-recorder
|
|
11
|
+
Project-URL: Issues, https://github.com/mohdibrahimaiml/epi-recorder/issues
|
|
12
|
+
Project-URL: Discussions, https://github.com/mohdibrahimaiml/epi-recorder/discussions
|
|
13
|
+
Keywords: evidence,forensics,audit,compliance,cryptography,ai,llm,verification,artifact,execution-trace,reproducibility,tamper-evident
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Legal Industry
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Security :: Cryptography
|
|
25
|
+
Classifier: Topic :: System :: Logging
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Classifier: Framework :: Pydantic
|
|
28
|
+
Classifier: Framework :: Pydantic :: 2
|
|
29
|
+
Requires-Python: >=3.11
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: pydantic>=2.0.0
|
|
33
|
+
Requires-Dist: cryptography>=41.0.0
|
|
34
|
+
Requires-Dist: cbor2>=5.6.0
|
|
35
|
+
Requires-Dist: typer[all]>=0.12.0
|
|
36
|
+
Requires-Dist: rich>=13.0.0
|
|
37
|
+
Requires-Dist: google-generativeai>=0.4.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: black>=24.0.0; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="https://raw.githubusercontent.com/mohdibrahimaiml/epi-recorder/main/docs/assets/logo.png" alt="EPI Logo" width="180"/>
|
|
48
|
+
<br>
|
|
49
|
+
<h1 align="center">EPI</h1>
|
|
50
|
+
<p align="center"><strong>Verifiable Execution Evidence for AI Systems / AI Agents</strong></p>
|
|
51
|
+
<p align="center">
|
|
52
|
+
<em>A portable, cryptographically sealed artifact format for AI execution records.</em>
|
|
53
|
+
</p>
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
<p align="center">
|
|
57
|
+
<a href="https://pypi.org/project/epi-recorder/"><img src="https://img.shields.io/pypi/v/epi-recorder?style=for-the-badge&color=00d4ff&label=PyPI" alt="PyPI"/></a>
|
|
58
|
+
<a href="https://github.com/mohdibrahimaiml/epi-recorder"><img src="https://img.shields.io/badge/python-3.11%2B-blue?style=for-the-badge&logo=python&logoColor=white" alt="Python"/></a>
|
|
59
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge" alt="License"/></a>
|
|
60
|
+
</p>
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## What is EPI?
|
|
65
|
+
|
|
66
|
+
EPI (Evidence Package for AI) is a **file format** for capturing and verifying AI execution evidence.
|
|
67
|
+
|
|
68
|
+
An `.epi` file is to AI execution what PDF is to documents:
|
|
69
|
+
- **Self-contained** — prompts, responses, environment, viewer — all in one file
|
|
70
|
+
- **Universally viewable** — opens in any browser, no software required
|
|
71
|
+
- **Tamper-evident** — Ed25519 signatures prove the record wasn't altered
|
|
72
|
+
|
|
73
|
+
EPI is designed for **adversarial review**: audits, incident response, compliance, litigation.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Design Guarantees
|
|
78
|
+
|
|
79
|
+
EPI artifacts provide the following guarantees:
|
|
80
|
+
|
|
81
|
+
| Guarantee | Implementation |
|
|
82
|
+
|:----------|:---------------|
|
|
83
|
+
| **Explicitness** | Evidence capture is intentional and reviewable in source code |
|
|
84
|
+
| **Portability** | Single file, no external dependencies, works offline |
|
|
85
|
+
| **Offline Verifiability** | Signature verification requires no network or cloud services |
|
|
86
|
+
| **Adversarial Review** | Format assumes the reviewer does not trust the producer |
|
|
87
|
+
|
|
88
|
+
These are not features. They are **constraints** that define what EPI is.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install epi-recorder
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Capture Evidence (Explicit API)
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from epi_recorder import record
|
|
102
|
+
|
|
103
|
+
with record("evidence.epi") as epi:
|
|
104
|
+
response = client.chat.completions.create(model="gpt-4", messages=[...])
|
|
105
|
+
epi.log_llm_call(response) # Explicit capture
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Capture Evidence (Wrapper Client)
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from epi_recorder import record, wrap_openai
|
|
112
|
+
from openai import OpenAI
|
|
113
|
+
|
|
114
|
+
client = wrap_openai(OpenAI())
|
|
115
|
+
|
|
116
|
+
with record("evidence.epi"):
|
|
117
|
+
response = client.chat.completions.create(...) # Captured via wrapper
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Verify Evidence
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
epi verify evidence.epi
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## The `.epi` Artifact Format
|
|
129
|
+
|
|
130
|
+
An `.epi` file is a ZIP archive with a defined structure. See [docs/EPI-SPEC.md](docs/EPI-SPEC.md) for the full specification.
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
evidence.epi
|
|
134
|
+
├── mimetype # "application/epi+zip"
|
|
135
|
+
├── manifest.json # Metadata + Ed25519 signature
|
|
136
|
+
├── steps.jsonl # Execution steps (NDJSON)
|
|
137
|
+
├── env.json # Runtime environment snapshot
|
|
138
|
+
└── viewer/
|
|
139
|
+
└── index.html # Self-contained offline viewer
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The embedded viewer allows any recipient to:
|
|
143
|
+
- View the complete execution timeline
|
|
144
|
+
- Verify cryptographic integrity
|
|
145
|
+
- Inspect individual steps
|
|
146
|
+
|
|
147
|
+
No software installation required.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## CLI Reference
|
|
152
|
+
|
|
153
|
+
### Primary Commands
|
|
154
|
+
|
|
155
|
+
| Command | Purpose |
|
|
156
|
+
|:--------|:--------|
|
|
157
|
+
| `epi run <script.py>` | Capture execution evidence to `.epi` |
|
|
158
|
+
| `epi verify <file.epi>` | Verify artifact integrity and signature |
|
|
159
|
+
| `epi view <file.epi>` | Open artifact in browser viewer |
|
|
160
|
+
| `epi keys list` | Manage signing keys |
|
|
161
|
+
|
|
162
|
+
### Secondary Tools
|
|
163
|
+
|
|
164
|
+
These tools consume evidence artifacts for analysis:
|
|
165
|
+
|
|
166
|
+
| Command | Purpose |
|
|
167
|
+
|:--------|:--------|
|
|
168
|
+
| `epi debug <file.epi>` | Heuristic analysis (loops, errors, inefficiencies) |
|
|
169
|
+
| `epi chat <file.epi>` | Natural language querying via LLM |
|
|
170
|
+
|
|
171
|
+
> **Note:** `debug` and `chat` are convenience tools built on top of the evidence format.
|
|
172
|
+
> They are not part of the core specification.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Cryptographic Properties
|
|
177
|
+
|
|
178
|
+
| Property | Implementation |
|
|
179
|
+
|:---------|:---------------|
|
|
180
|
+
| **Signatures** | Ed25519 (RFC 8032) |
|
|
181
|
+
| **Hashing** | SHA-256 content addressing |
|
|
182
|
+
| **Key Storage** | Local keyring, user-controlled |
|
|
183
|
+
| **Verification** | Client-side, zero external dependencies |
|
|
184
|
+
|
|
185
|
+
Signatures are **optional but recommended**. Unsigned artifacts are still valid but cannot prove origin.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## When to Use EPI
|
|
190
|
+
|
|
191
|
+
### Appropriate Use Cases
|
|
192
|
+
|
|
193
|
+
- Post-incident forensics
|
|
194
|
+
- Compliance documentation
|
|
195
|
+
- Audit trails for autonomous systems
|
|
196
|
+
- Reproducibility evidence for research
|
|
197
|
+
- Litigation-grade execution records
|
|
198
|
+
|
|
199
|
+
### Not Designed For
|
|
200
|
+
|
|
201
|
+
- Real-time monitoring dashboards
|
|
202
|
+
- High-frequency telemetry
|
|
203
|
+
- System health metrics
|
|
204
|
+
- Application performance monitoring
|
|
205
|
+
|
|
206
|
+
EPI produces **artifacts**, not **streams**.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Supported Providers
|
|
211
|
+
|
|
212
|
+
| Provider | Capture Method |
|
|
213
|
+
|:---------|:---------------|
|
|
214
|
+
| OpenAI | Wrapper client or explicit API |
|
|
215
|
+
| Anthropic | Explicit API |
|
|
216
|
+
| Google Gemini | Explicit API |
|
|
217
|
+
| Any HTTP-based LLM | Explicit API via `log_llm_call()` |
|
|
218
|
+
|
|
219
|
+
EPI does not depend on provider-specific integrations. The explicit API works with any response format.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## v2.3.0 — Design Correction
|
|
224
|
+
|
|
225
|
+
This release corrects EPI's evidence capture model.
|
|
226
|
+
|
|
227
|
+
| Before (v2.2.x) | After (v2.3.0) |
|
|
228
|
+
|:----------------|:---------------|
|
|
229
|
+
| Implicit monkey-patching | Explicit capture |
|
|
230
|
+
| Fragile to SDK changes | Stable across versions |
|
|
231
|
+
| Hidden instrumentation | Reviewable in source |
|
|
232
|
+
|
|
233
|
+
**Rationale:** Evidence systems must be intentional. Implicit capture was convenient but violated the explicitness guarantee.
|
|
234
|
+
|
|
235
|
+
**Migration:** Replace implicit capture with `epi.log_llm_call(response)` or `wrap_openai()`.
|
|
236
|
+
|
|
237
|
+
**Legacy mode:** `record(legacy_patching=True)` is deprecated and will be removed in v3.0.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Release History
|
|
242
|
+
|
|
243
|
+
| Version | Date | Summary |
|
|
244
|
+
|:--------|:-----|:--------|
|
|
245
|
+
| **2.3.0** | 2026-02-06 | Explicit capture, wrapper clients, design correction |
|
|
246
|
+
| **2.2.0** | 2026-01-30 | SQLite storage, async support, context isolation |
|
|
247
|
+
| **2.1.3** | 2026-01-24 | Gemini capture, evidence querying |
|
|
248
|
+
| **2.1.0** | 2025-12-15 | Initial release |
|
|
249
|
+
|
|
250
|
+
See [CHANGELOG.md](./CHANGELOG.md) for full details.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Contributing
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
git clone https://github.com/mohdibrahimaiml/epi-recorder.git
|
|
258
|
+
cd epi-recorder
|
|
259
|
+
pip install -e ".[dev]"
|
|
260
|
+
pytest
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT License. See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
epi_analyzer/__init__.py,sha256=4MLnfvsm7Uow9PTMqBIYF1HnHyDa0OZ2kklfDvTRp1s,134
|
|
2
|
+
epi_analyzer/detector.py,sha256=JrZ7NGmG0vWb2Vskh-U_S1KYkyVjscUywwu2roe1HCQ,13665
|
|
3
|
+
epi_cli/__init__.py,sha256=KEh3YUH01d0w7B-56gEIgI87jHh-UDuIkxzVSfzl7y4,104
|
|
4
|
+
epi_cli/__main__.py,sha256=HzwyIuqH0lO6pJMApM8ZsXcNzmq9wIKtWM1zJvzVSzY,302
|
|
5
|
+
epi_cli/chat.py,sha256=D7ULAbciCPi_2rcGyoUmSYGGD0ceYYVcw4ekGSKoTQc,7400
|
|
6
|
+
epi_cli/debug.py,sha256=khGJ2xiohQYUgyTBJE5iZM-w1pKA0nou6VJPvmORKF4,3794
|
|
7
|
+
epi_cli/keys.py,sha256=3EZaNc-NvHNWWeHfxKTkZs3bUhOdw7sJ3X2_021__gE,9117
|
|
8
|
+
epi_cli/ls.py,sha256=ijFZdnzb8ncDFmNG7-j_mg7xLkXMJ_Cd_AwIE4tE7gI,5023
|
|
9
|
+
epi_cli/main.py,sha256=NXzJb2dt0R5zLIcaiy1yzdxbRcAbKWv1Oxr4tRZ4vBE,12366
|
|
10
|
+
epi_cli/record.py,sha256=bmUNr2cELwo6qVKbFvWlI2HpIbcGzXcM1MT2-Fs2cxI,7327
|
|
11
|
+
epi_cli/run.py,sha256=JHTL_sm1LN02IiaWBbM7vBwybzrrJbwsQsUxXg13fDY,14376
|
|
12
|
+
epi_cli/verify.py,sha256=9zr5gNH0v70Ngg_5F_JuFZQcUzWQ3YhH9WFlfUS1I0o,8244
|
|
13
|
+
epi_cli/view.py,sha256=EP9takENuZnRllBsxDze9Mm32TGsyxsQaUhlNmUNA_w,4027
|
|
14
|
+
epi_core/__init__.py,sha256=8CTVjxZDI6oy-MMqWTILY9h8rgSZXS8kVzgySympGJU,309
|
|
15
|
+
epi_core/container.py,sha256=Eop4CN3TgCoxRyEWorbjvVBnFaxS4zkccdDwgXQ4eIk,13344
|
|
16
|
+
epi_core/redactor.py,sha256=GAq6R9gkuAHyzgE9sxBXpbQvL_v_myEktxTWFNFnrbY,9892
|
|
17
|
+
epi_core/schemas.py,sha256=iVz6yqVnXEe_mG5wh6Y4ojGHyop_kB5igpKXRkK7fT8,4734
|
|
18
|
+
epi_core/serialize.py,sha256=KB7Z7dfDFh6qq0tlrwjWADOBUV4z32q29Dt2yiniGGg,5691
|
|
19
|
+
epi_core/storage.py,sha256=XEVbdr5xf00LDDJMqCdrZDFvVS-BZ1e1CWzDaJqG0jE,5374
|
|
20
|
+
epi_core/trust.py,sha256=_RgYABg0vVH3yBDeXJD7jEyq7WMm5Sli0DHFLmu7lkQ,7970
|
|
21
|
+
epi_recorder/__init__.py,sha256=tCq2jrJHLxOfO4FPJL3W7F9dRIiOmbNbOZ52cUnqa90,565
|
|
22
|
+
epi_recorder/api.py,sha256=HK10VkaOLxdi-QSw4Jb_NfW-_p0FtXqWQqTJT-rOvlQ,29820
|
|
23
|
+
epi_recorder/async_api.py,sha256=a2WQL8MnJ8uwnLD6unDZxASe5JbywP1V-8gcFyySFM8,4949
|
|
24
|
+
epi_recorder/bootstrap.py,sha256=vk6mKnaHcnanm8SB7dYGPDJ8E2iSBSX3OTQ3zyO-6b0,1851
|
|
25
|
+
epi_recorder/environment.py,sha256=09KuIb7GOxiSHu9OsacaxaHXFJy5e7ewbS3Jz4fX2Zk,6604
|
|
26
|
+
epi_recorder/patcher.py,sha256=U_1MRMhLtcdrYxDdD3Lt6MZ4nolkSiYIa93Vn1Go4yI,20981
|
|
27
|
+
epi_recorder/test_import.py,sha256=_wrlfu0BLtT21AINf1_NugJTvM-RVNKJOyzokMezjO0,462
|
|
28
|
+
epi_recorder/test_script.py,sha256=ot2vRtgvUdeqk6Oj_cz0TZyQN9fUFVHy2E82jdzZUOs,95
|
|
29
|
+
epi_recorder/wrappers/__init__.py,sha256=uG0jSBjM_wYo2_lmEiHzDtJbSgF0XlguaNye-dzVjPY,409
|
|
30
|
+
epi_recorder/wrappers/base.py,sha256=uZNNMJSgkf50J_Z-VYO9HOPk-1A1J00BivvKET4opIk,2641
|
|
31
|
+
epi_recorder/wrappers/openai.py,sha256=Mc5LxUSBQtaFyCh7MG0kwe8Wt0VM17weOCRh0N3vjuY,5977
|
|
32
|
+
epi_recorder-2.3.0.dist-info/licenses/LICENSE,sha256=C9g69QrsraEzIORyleKkUeim8h49747brp68ZUf9_ms,1089
|
|
33
|
+
epi_viewer_static/app.js,sha256=2CkRO3iq4J0QTpFgpfAvzGQDrrAIXGp61cJfIC225Cc,20362
|
|
34
|
+
epi_viewer_static/crypto.js,sha256=2bdANR9tLCPRE9joOih4kKVtptpfRXxERNps4IEhjAQ,19082
|
|
35
|
+
epi_viewer_static/index.html,sha256=sPNXnDTnk0ArVLofdKB3hhd8q-NL1AUmjucytXoythk,3302
|
|
36
|
+
epi_viewer_static/viewer_lite.css,sha256=EGsbTiaSZcnep5GMXm6eKxsfr9oIg_IjEDDI94KI4vc,4695
|
|
37
|
+
epi_recorder-2.3.0.dist-info/METADATA,sha256=9ZGZtvoz7vNiy6e35cB9AQUqRWpNNDQGHPqEZ_bfl1Q,8761
|
|
38
|
+
epi_recorder-2.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
39
|
+
epi_recorder-2.3.0.dist-info/entry_points.txt,sha256=MfMwqVRx_yMGbuPpiyjz2f8fQp8TUbHmRC1H_bupoyM,41
|
|
40
|
+
epi_recorder-2.3.0.dist-info/top_level.txt,sha256=osrjwlhDfJZSucB-G1u-rF6o0L1OCx2d892gSWr8Iik,77
|
|
41
|
+
epi_recorder-2.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 EPI Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
epi_viewer_static/app.js
CHANGED
|
@@ -37,18 +37,20 @@ async function renderTrustBadge(manifest) {
|
|
|
37
37
|
`;
|
|
38
38
|
|
|
39
39
|
// Check verification logic availability
|
|
40
|
+
const hasSignature = manifest.signature && manifest.signature !== "null" && manifest.signature.trim() !== "";
|
|
41
|
+
|
|
40
42
|
if (typeof window.verifyManifestSignature !== 'function') {
|
|
41
|
-
renderBadgeResult(false, 'Missing crypto lib',
|
|
43
|
+
renderBadgeResult(false, 'Missing crypto lib', hasSignature);
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
try {
|
|
46
48
|
const result = await window.verifyManifestSignature(manifest);
|
|
47
49
|
console.log("Verification Result:", result);
|
|
48
|
-
renderBadgeResult(result.valid, result.reason,
|
|
50
|
+
renderBadgeResult(result.valid, result.reason, hasSignature);
|
|
49
51
|
} catch (e) {
|
|
50
52
|
console.error("Verification error:", e);
|
|
51
|
-
renderBadgeResult(false, e.message,
|
|
53
|
+
renderBadgeResult(false, e.message, hasSignature);
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -211,7 +213,12 @@ function renderStep(step) {
|
|
|
211
213
|
</span>
|
|
212
214
|
<span class="text-sm font-medium text-gray-900">${kind}</span>
|
|
213
215
|
</div>
|
|
214
|
-
<
|
|
216
|
+
<div class="flex items-center space-x-2">
|
|
217
|
+
<span class="text-xs text-gray-500">${time}</span>
|
|
218
|
+
<button onclick='copyStepData(${JSON.stringify(JSON.stringify(content))})' class="text-gray-400 hover:text-blue-600 transition-colors" title="Copy Raw JSON">
|
|
219
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"></path></svg>
|
|
220
|
+
</button>
|
|
221
|
+
</div>
|
|
215
222
|
`;
|
|
216
223
|
wrapper.appendChild(header);
|
|
217
224
|
|
|
@@ -223,6 +230,14 @@ function renderStep(step) {
|
|
|
223
230
|
contentDiv.innerHTML = renderLLMRequest(content);
|
|
224
231
|
} else if (kind === 'llm.response') {
|
|
225
232
|
contentDiv.innerHTML = renderLLMResponse(content);
|
|
233
|
+
} else if (kind === 'llm.error') {
|
|
234
|
+
contentDiv.innerHTML = renderLLMError(content);
|
|
235
|
+
} else if (kind === 'http.request') {
|
|
236
|
+
contentDiv.innerHTML = renderHTTPRequest(content);
|
|
237
|
+
} else if (kind === 'http.response') {
|
|
238
|
+
contentDiv.innerHTML = renderHTTPResponse(content);
|
|
239
|
+
} else if (kind === 'http.error') {
|
|
240
|
+
contentDiv.innerHTML = renderHTTPError(content);
|
|
226
241
|
} else if (kind === 'security.redaction') {
|
|
227
242
|
contentDiv.innerHTML = renderRedaction(content);
|
|
228
243
|
} else {
|
|
@@ -283,7 +298,7 @@ function renderLLMResponse(content) {
|
|
|
283
298
|
html += `
|
|
284
299
|
<div class="chat-bubble mr-auto bg-green-100 text-green-900 rounded-lg px-4 py-2 text-sm">
|
|
285
300
|
<div class="text-xs font-medium mb-1 uppercase">Assistant</div>
|
|
286
|
-
<div class="whitespace-pre-wrap">${
|
|
301
|
+
<div class="whitespace-pre-wrap">${formatMessageContent(choice.message.content)}</div>
|
|
287
302
|
${choice.finish_reason ? `<div class="text-xs text-green-700 mt-2">• ${choice.finish_reason}</div>` : ''}
|
|
288
303
|
</div>
|
|
289
304
|
`;
|
|
@@ -296,7 +311,7 @@ function renderLLMResponse(content) {
|
|
|
296
311
|
html += `
|
|
297
312
|
<div class="mt-3 text-xs text-gray-600 flex items-center space-x-4">
|
|
298
313
|
<span>📊 ${content.usage.total_tokens} tokens</span>
|
|
299
|
-
|
|
314
|
+
${content.latency_seconds ? `<span>⚡ ${content.latency_seconds}s</span>` : ''}
|
|
300
315
|
</div>
|
|
301
316
|
`;
|
|
302
317
|
}
|
|
@@ -321,6 +336,74 @@ function renderRedaction(content) {
|
|
|
321
336
|
`;
|
|
322
337
|
}
|
|
323
338
|
|
|
339
|
+
// Render LLM error
|
|
340
|
+
function renderLLMError(content) {
|
|
341
|
+
return `
|
|
342
|
+
<div class="bg-red-50 border border-red-200 rounded-lg p-3 text-sm">
|
|
343
|
+
<div class="flex items-center text-red-800">
|
|
344
|
+
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
|
345
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
|
346
|
+
</svg>
|
|
347
|
+
<span class="font-medium">LLM Error</span>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="mt-2 text-red-700 font-mono text-xs">
|
|
350
|
+
${escapeHTML(content.error || content.message || JSON.stringify(content))}
|
|
351
|
+
</div>
|
|
352
|
+
${content.provider ? `<div class="mt-1 text-xs text-red-600">${content.provider} • ${content.model || 'unknown'}</div>` : ''}
|
|
353
|
+
</div>
|
|
354
|
+
`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Render HTTP request
|
|
358
|
+
function renderHTTPRequest(content) {
|
|
359
|
+
return `
|
|
360
|
+
<div class="bg-indigo-50 border border-indigo-200 rounded-lg p-3 text-sm">
|
|
361
|
+
<div class="flex items-center text-indigo-800 mb-2">
|
|
362
|
+
<span class="font-medium px-2 py-0.5 bg-indigo-200 rounded text-xs">${content.method || 'GET'}</span>
|
|
363
|
+
<span class="ml-2 font-mono text-xs break-all">${escapeHTML(content.url || '')}</span>
|
|
364
|
+
</div>
|
|
365
|
+
${content.headers ? `<div class="text-xs text-indigo-600">Headers: ${Object.keys(content.headers).length}</div>` : ''}
|
|
366
|
+
${content.body ? `<pre class="mt-2 text-xs bg-indigo-100 p-2 rounded overflow-auto max-h-32">${escapeHTML(typeof content.body === 'string' ? content.body : JSON.stringify(content.body, null, 2))}</pre>` : ''}
|
|
367
|
+
</div>
|
|
368
|
+
`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Render HTTP response
|
|
372
|
+
function renderHTTPResponse(content) {
|
|
373
|
+
const statusColor = (content.status_code >= 200 && content.status_code < 300) ? 'green' :
|
|
374
|
+
(content.status_code >= 400) ? 'red' : 'yellow';
|
|
375
|
+
return `
|
|
376
|
+
<div class="bg-${statusColor}-50 border border-${statusColor}-200 rounded-lg p-3 text-sm">
|
|
377
|
+
<div class="flex items-center text-${statusColor}-800 mb-2">
|
|
378
|
+
<span class="font-medium px-2 py-0.5 bg-${statusColor}-200 rounded text-xs">${content.status_code || '???'}</span>
|
|
379
|
+
<span class="ml-2 text-xs">${content.url || ''}</span>
|
|
380
|
+
${content.latency_seconds ? `<span class="ml-auto text-xs">⚡ ${content.latency_seconds}s</span>` : ''}
|
|
381
|
+
</div>
|
|
382
|
+
${content.body ? `<pre class="mt-2 text-xs bg-gray-100 p-2 rounded overflow-auto max-h-32">${escapeHTML(typeof content.body === 'string' ? content.body.slice(0, 500) : JSON.stringify(content.body, null, 2).slice(0, 500))}${(content.body.length > 500) ? '...' : ''}</pre>` : ''}
|
|
383
|
+
</div>
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Render HTTP error
|
|
388
|
+
function renderHTTPError(content) {
|
|
389
|
+
return `
|
|
390
|
+
<div class="bg-red-50 border border-red-200 rounded-lg p-3 text-sm">
|
|
391
|
+
<div class="flex items-center text-red-800">
|
|
392
|
+
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
|
393
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
|
394
|
+
</svg>
|
|
395
|
+
<span class="font-medium">HTTP Error</span>
|
|
396
|
+
</div>
|
|
397
|
+
<div class="mt-2 text-red-700">
|
|
398
|
+
<span class="font-mono text-xs">${escapeHTML(content.url || '')}</span>
|
|
399
|
+
</div>
|
|
400
|
+
<div class="mt-1 text-red-600 text-xs">
|
|
401
|
+
${escapeHTML(content.error || content.message || JSON.stringify(content))}
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
406
|
+
|
|
324
407
|
// Escape HTML to prevent XSS
|
|
325
408
|
function escapeHTML(str) {
|
|
326
409
|
const div = document.createElement('div');
|
|
@@ -348,6 +431,28 @@ function renderTimeline(steps) {
|
|
|
348
431
|
}
|
|
349
432
|
}
|
|
350
433
|
|
|
434
|
+
// Helper: Format message content with bolding
|
|
435
|
+
function formatMessageContent(text) {
|
|
436
|
+
if (!text) return '';
|
|
437
|
+
// Escape HTML first
|
|
438
|
+
let escaped = escapeHTML(text);
|
|
439
|
+
// Apply bold formatting for **text**
|
|
440
|
+
return escaped.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Helper: Copy to clipboard
|
|
444
|
+
window.copyStepData = function (dataStr) {
|
|
445
|
+
try {
|
|
446
|
+
const data = JSON.parse(dataStr); // It was doubly stringified
|
|
447
|
+
navigator.clipboard.writeText(JSON.stringify(data, null, 2)).then(() => {
|
|
448
|
+
// Visual feedback could be added here
|
|
449
|
+
console.log('Copied to clipboard');
|
|
450
|
+
});
|
|
451
|
+
} catch (e) {
|
|
452
|
+
console.error('Copy failed', e);
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
351
456
|
// Initialize viewer
|
|
352
457
|
async function init() {
|
|
353
458
|
const data = loadEPIData();
|
|
@@ -374,4 +479,5 @@ if (document.readyState === 'loading') {
|
|
|
374
479
|
document.addEventListener('DOMContentLoaded', init);
|
|
375
480
|
} else {
|
|
376
481
|
init();
|
|
377
|
-
}
|
|
482
|
+
}
|
|
483
|
+
|
epi_viewer_static/crypto.js
CHANGED
epi_viewer_static/index.html
CHANGED
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
<!-- Footer -->
|
|
95
95
|
<footer class="mt-12 bg-white border-t border-gray-200">
|
|
96
96
|
<div class="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
|
97
|
-
EPI v2.
|
|
97
|
+
EPI v2.2.0 | <span class="font-mono">application/epi+zip</span>
|
|
98
98
|
</div>
|
|
99
99
|
</footer>
|
|
100
100
|
</div>
|
|
@@ -102,4 +102,6 @@
|
|
|
102
102
|
<script src="app.js"></script>
|
|
103
103
|
</body>
|
|
104
104
|
|
|
105
|
-
</html>
|
|
105
|
+
</html>
|
|
106
|
+
|
|
107
|
+
|