methodproof 0.5.3__tar.gz → 0.6.0__tar.gz
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.
- {methodproof-0.5.3 → methodproof-0.6.0}/PKG-INFO +89 -6
- {methodproof-0.5.3 → methodproof-0.6.0}/README.md +88 -5
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/__init__.py +1 -1
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/_daemon.py +2 -1
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/agents/base.py +7 -7
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/bridge.py +13 -5
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/cli.py +1 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/config.py +2 -3
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/sync.py +15 -5
- {methodproof-0.5.3 → methodproof-0.6.0}/pyproject.toml +1 -1
- {methodproof-0.5.3 → methodproof-0.6.0}/uv.lock +2 -2
- {methodproof-0.5.3 → methodproof-0.6.0}/.github/workflows/ci.yml +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/.gitignore +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/CHANGELOG.md +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/LICENSE +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/__main__.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/agents/__init__.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/agents/music.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/agents/terminal.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/agents/watcher.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/analysis.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/binding.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/bip39.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/crypto.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/graph.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hook.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/__init__.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/claude_code.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/claude_code.sh +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/cline_hook.sh +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/codex_hook.sh +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/gemini_hook.sh +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/install.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/kiro_hook.sh +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/mcp_register.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/openclaw/HOOK.md +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/openclaw/handler.ts +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/openclaw_install.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/opencode_plugin.js +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/hooks/wrappers.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/integrity.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/kdf.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/keychain.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/live.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/lock.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/mcp.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/migrate_db.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/proxy.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/proxy_daemon.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/repos.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/skills/methodproof/SKILL.md +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/store.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/viewer.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/methodproof/wordlist.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/test_windows_compat.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/__init__.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_analysis.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_graph.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_hooks.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_live.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_openclaw_hooks.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_security.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_store.py +0 -0
- {methodproof-0.5.3 → methodproof-0.6.0}/tests/test_wrappers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: methodproof
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: See how you code. Capture and visualize your engineering process.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -67,6 +67,87 @@ methodproof view # explore your session in the browser
|
|
|
67
67
|
- **Auto-detection** — hooks for shell, Claude Code, OpenClaw, codex, gemini, aider installed automatically
|
|
68
68
|
- **Platform sync** — `methodproof push` uploads sessions. `methodproof publish` makes them public and shareable
|
|
69
69
|
|
|
70
|
+
## Security Architecture
|
|
71
|
+
|
|
72
|
+
Every event passes through consent gating, encryption, and hash chaining before touching disk. The platform adds a server co-signature on push, creating two-party evidence.
|
|
73
|
+
|
|
74
|
+
```mermaid
|
|
75
|
+
flowchart TB
|
|
76
|
+
subgraph LOCAL["LOCAL MACHINE"]
|
|
77
|
+
direction TB
|
|
78
|
+
subgraph AGENTS["Capture Agents"]
|
|
79
|
+
direction LR
|
|
80
|
+
W["File Watcher"] & T["Terminal Hook"] & B["Browser Bridge"] & H["AI Tool Hooks"]
|
|
81
|
+
end
|
|
82
|
+
W & T & B & H -->|"raw event"| EMIT["emit(type, metadata)"]
|
|
83
|
+
|
|
84
|
+
subgraph CONSENT["Consent Layer"]
|
|
85
|
+
direction TB
|
|
86
|
+
EMIT --> EG{"Event Gate"}
|
|
87
|
+
EG -->|"category OFF"| DROP["Dropped"]
|
|
88
|
+
EG -->|"category ON"| FG["Field Gate: strip opted-out fields"]
|
|
89
|
+
FG --> JG{"Journal Mode?"}
|
|
90
|
+
JG -->|"OFF: structural only"| STRIP["Strip content fields"]
|
|
91
|
+
JG -->|"ON: full content"| KEEP["Keep all fields"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
subgraph CRYPTO["Encryption + Integrity"]
|
|
95
|
+
direction TB
|
|
96
|
+
STRIP & KEEP --> AES["AES-256-GCM: encrypt sensitive fields"]
|
|
97
|
+
AES --> LOCK["Thread Lock: atomic hash + buffer"]
|
|
98
|
+
LOCK --> CHAIN["SHA-256 Hash Chain"]
|
|
99
|
+
CHAIN --> BUF["Batched Flush"]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
BUF --> DB[("SQLite WAL")]
|
|
103
|
+
BUF -.->|"--live"| WS["WebSocket Stream"]
|
|
104
|
+
|
|
105
|
+
subgraph KEYS["Key Vault"]
|
|
106
|
+
direction LR
|
|
107
|
+
ENT["Entropy"] --> KDF["Key Derivation"]
|
|
108
|
+
KDF --> DBK["Encryption Key"]
|
|
109
|
+
KDF --> BINDK["Binding Key"]
|
|
110
|
+
KDF --> ED25["Signing Key"]
|
|
111
|
+
ENT -.-> REC["Recovery Phrase"]
|
|
112
|
+
KDF -.-> KC["OS Keychain"]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
subgraph PUSH["PUSH PROTOCOL"]
|
|
117
|
+
direction TB
|
|
118
|
+
DB --> BATCH["Batched Upload"]
|
|
119
|
+
BATCH --> BIND["Session Binding: HMAC over session metadata"]
|
|
120
|
+
BIND --> SIGN["Ed25519 Sign: session summary"]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
subgraph PLATFORM["PLATFORM TRUST BOUNDARY"]
|
|
124
|
+
direction TB
|
|
125
|
+
SIGN --> VERIFY["Verify Ed25519 against registered public key"]
|
|
126
|
+
VERIFY --> COSIG["Server Co-Sign: HMAC over attestation receipt"]
|
|
127
|
+
COSIG --> NEO[("Graph Store")]
|
|
128
|
+
NEO --> SCORE["Integrity Score: chain + attestation + cosig + git xref + anomaly"]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
style DROP fill:#d93326,stroke:#d93326,color:#fff
|
|
132
|
+
style AES fill:#803794,stroke:#803794,color:#fff
|
|
133
|
+
style CHAIN fill:#803794,stroke:#803794,color:#fff
|
|
134
|
+
style LOCK fill:#803794,stroke:#803794,color:#fff
|
|
135
|
+
style EG fill:#109446,stroke:#109446,color:#fff
|
|
136
|
+
style JG fill:#109446,stroke:#109446,color:#fff
|
|
137
|
+
style STRIP fill:#109446,stroke:#109446,color:#fff
|
|
138
|
+
style VERIFY fill:#c9a84c,stroke:#c9a84c,color:#fff
|
|
139
|
+
style COSIG fill:#c9a84c,stroke:#c9a84c,color:#fff
|
|
140
|
+
style SCORE fill:#c9a84c,stroke:#c9a84c,color:#fff
|
|
141
|
+
style SIGN fill:#192a56,stroke:#192a56,color:#fff
|
|
142
|
+
style BIND fill:#192a56,stroke:#192a56,color:#fff
|
|
143
|
+
style DB fill:#192a56,stroke:#192a56,color:#fff
|
|
144
|
+
style NEO fill:#192a56,stroke:#192a56,color:#fff
|
|
145
|
+
style KC fill:#803794,stroke:#803794,color:#fff
|
|
146
|
+
style REC fill:#803794,stroke:#803794,color:#fff
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
<sup>Green = consent gates · Purple = cryptographic operations · Navy = storage + binding · Gold = integrity verification</sup>
|
|
150
|
+
|
|
70
151
|
## Commands
|
|
71
152
|
|
|
72
153
|
| Command | What it does |
|
|
@@ -167,13 +248,15 @@ At session end, outcome metrics are computed: first-shot apply rate, follow-up s
|
|
|
167
248
|
<details>
|
|
168
249
|
<summary>Integrity verification</summary>
|
|
169
250
|
|
|
170
|
-
|
|
251
|
+
Four layers ensure session data hasn't been tampered with (see [architecture diagram](#security-architecture) above):
|
|
252
|
+
|
|
253
|
+
**Hash-chained events** — every event includes a SHA-256 hash linking to the previous event. Hash computation is thread-safe (atomic with buffer append). Any modification breaks the chain, detectable via `GET /sessions/{id}/chain/verify`.
|
|
171
254
|
|
|
172
|
-
**
|
|
255
|
+
**Ed25519 attestation** — on `methodproof push`, the CLI signs a session summary with your private key. Key generated during `methodproof init`, stored in `~/.methodproof/`.
|
|
173
256
|
|
|
174
|
-
**
|
|
257
|
+
**Server co-signature** — the platform independently signs the attestation receipt with its own key and timestamp, creating two-party evidence. Sessions pushed without server co-signature receive a lower integrity score.
|
|
175
258
|
|
|
176
|
-
**Binary hash self-reporting** — the CLI reports its own
|
|
259
|
+
**Binary hash self-reporting** — the CLI reports its own source hash on push. The platform compares against known release hashes to detect modified builds.
|
|
177
260
|
|
|
178
261
|
</details>
|
|
179
262
|
|
|
@@ -222,7 +305,7 @@ methodproof start
|
|
|
222
305
|
|
|
223
306
|
**Excluded patterns:** `__pycache__`, `.pyc`, `.git/`, `node_modules`, `.DS_Store`, `.swp`, temp files ending in `~`
|
|
224
307
|
|
|
225
|
-
**Git commits** are detected
|
|
308
|
+
**Git commits** are detected automatically — only commits in a git repo rooted at (or above) the watch directory are captured.
|
|
226
309
|
|
|
227
310
|
## Data Directory
|
|
228
311
|
|
|
@@ -52,6 +52,87 @@ methodproof view # explore your session in the browser
|
|
|
52
52
|
- **Auto-detection** — hooks for shell, Claude Code, OpenClaw, codex, gemini, aider installed automatically
|
|
53
53
|
- **Platform sync** — `methodproof push` uploads sessions. `methodproof publish` makes them public and shareable
|
|
54
54
|
|
|
55
|
+
## Security Architecture
|
|
56
|
+
|
|
57
|
+
Every event passes through consent gating, encryption, and hash chaining before touching disk. The platform adds a server co-signature on push, creating two-party evidence.
|
|
58
|
+
|
|
59
|
+
```mermaid
|
|
60
|
+
flowchart TB
|
|
61
|
+
subgraph LOCAL["LOCAL MACHINE"]
|
|
62
|
+
direction TB
|
|
63
|
+
subgraph AGENTS["Capture Agents"]
|
|
64
|
+
direction LR
|
|
65
|
+
W["File Watcher"] & T["Terminal Hook"] & B["Browser Bridge"] & H["AI Tool Hooks"]
|
|
66
|
+
end
|
|
67
|
+
W & T & B & H -->|"raw event"| EMIT["emit(type, metadata)"]
|
|
68
|
+
|
|
69
|
+
subgraph CONSENT["Consent Layer"]
|
|
70
|
+
direction TB
|
|
71
|
+
EMIT --> EG{"Event Gate"}
|
|
72
|
+
EG -->|"category OFF"| DROP["Dropped"]
|
|
73
|
+
EG -->|"category ON"| FG["Field Gate: strip opted-out fields"]
|
|
74
|
+
FG --> JG{"Journal Mode?"}
|
|
75
|
+
JG -->|"OFF: structural only"| STRIP["Strip content fields"]
|
|
76
|
+
JG -->|"ON: full content"| KEEP["Keep all fields"]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
subgraph CRYPTO["Encryption + Integrity"]
|
|
80
|
+
direction TB
|
|
81
|
+
STRIP & KEEP --> AES["AES-256-GCM: encrypt sensitive fields"]
|
|
82
|
+
AES --> LOCK["Thread Lock: atomic hash + buffer"]
|
|
83
|
+
LOCK --> CHAIN["SHA-256 Hash Chain"]
|
|
84
|
+
CHAIN --> BUF["Batched Flush"]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
BUF --> DB[("SQLite WAL")]
|
|
88
|
+
BUF -.->|"--live"| WS["WebSocket Stream"]
|
|
89
|
+
|
|
90
|
+
subgraph KEYS["Key Vault"]
|
|
91
|
+
direction LR
|
|
92
|
+
ENT["Entropy"] --> KDF["Key Derivation"]
|
|
93
|
+
KDF --> DBK["Encryption Key"]
|
|
94
|
+
KDF --> BINDK["Binding Key"]
|
|
95
|
+
KDF --> ED25["Signing Key"]
|
|
96
|
+
ENT -.-> REC["Recovery Phrase"]
|
|
97
|
+
KDF -.-> KC["OS Keychain"]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
subgraph PUSH["PUSH PROTOCOL"]
|
|
102
|
+
direction TB
|
|
103
|
+
DB --> BATCH["Batched Upload"]
|
|
104
|
+
BATCH --> BIND["Session Binding: HMAC over session metadata"]
|
|
105
|
+
BIND --> SIGN["Ed25519 Sign: session summary"]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
subgraph PLATFORM["PLATFORM TRUST BOUNDARY"]
|
|
109
|
+
direction TB
|
|
110
|
+
SIGN --> VERIFY["Verify Ed25519 against registered public key"]
|
|
111
|
+
VERIFY --> COSIG["Server Co-Sign: HMAC over attestation receipt"]
|
|
112
|
+
COSIG --> NEO[("Graph Store")]
|
|
113
|
+
NEO --> SCORE["Integrity Score: chain + attestation + cosig + git xref + anomaly"]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
style DROP fill:#d93326,stroke:#d93326,color:#fff
|
|
117
|
+
style AES fill:#803794,stroke:#803794,color:#fff
|
|
118
|
+
style CHAIN fill:#803794,stroke:#803794,color:#fff
|
|
119
|
+
style LOCK fill:#803794,stroke:#803794,color:#fff
|
|
120
|
+
style EG fill:#109446,stroke:#109446,color:#fff
|
|
121
|
+
style JG fill:#109446,stroke:#109446,color:#fff
|
|
122
|
+
style STRIP fill:#109446,stroke:#109446,color:#fff
|
|
123
|
+
style VERIFY fill:#c9a84c,stroke:#c9a84c,color:#fff
|
|
124
|
+
style COSIG fill:#c9a84c,stroke:#c9a84c,color:#fff
|
|
125
|
+
style SCORE fill:#c9a84c,stroke:#c9a84c,color:#fff
|
|
126
|
+
style SIGN fill:#192a56,stroke:#192a56,color:#fff
|
|
127
|
+
style BIND fill:#192a56,stroke:#192a56,color:#fff
|
|
128
|
+
style DB fill:#192a56,stroke:#192a56,color:#fff
|
|
129
|
+
style NEO fill:#192a56,stroke:#192a56,color:#fff
|
|
130
|
+
style KC fill:#803794,stroke:#803794,color:#fff
|
|
131
|
+
style REC fill:#803794,stroke:#803794,color:#fff
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
<sup>Green = consent gates · Purple = cryptographic operations · Navy = storage + binding · Gold = integrity verification</sup>
|
|
135
|
+
|
|
55
136
|
## Commands
|
|
56
137
|
|
|
57
138
|
| Command | What it does |
|
|
@@ -152,13 +233,15 @@ At session end, outcome metrics are computed: first-shot apply rate, follow-up s
|
|
|
152
233
|
<details>
|
|
153
234
|
<summary>Integrity verification</summary>
|
|
154
235
|
|
|
155
|
-
|
|
236
|
+
Four layers ensure session data hasn't been tampered with (see [architecture diagram](#security-architecture) above):
|
|
237
|
+
|
|
238
|
+
**Hash-chained events** — every event includes a SHA-256 hash linking to the previous event. Hash computation is thread-safe (atomic with buffer append). Any modification breaks the chain, detectable via `GET /sessions/{id}/chain/verify`.
|
|
156
239
|
|
|
157
|
-
**
|
|
240
|
+
**Ed25519 attestation** — on `methodproof push`, the CLI signs a session summary with your private key. Key generated during `methodproof init`, stored in `~/.methodproof/`.
|
|
158
241
|
|
|
159
|
-
**
|
|
242
|
+
**Server co-signature** — the platform independently signs the attestation receipt with its own key and timestamp, creating two-party evidence. Sessions pushed without server co-signature receive a lower integrity score.
|
|
160
243
|
|
|
161
|
-
**Binary hash self-reporting** — the CLI reports its own
|
|
244
|
+
**Binary hash self-reporting** — the CLI reports its own source hash on push. The platform compares against known release hashes to detect modified builds.
|
|
162
245
|
|
|
163
246
|
</details>
|
|
164
247
|
|
|
@@ -207,7 +290,7 @@ methodproof start
|
|
|
207
290
|
|
|
208
291
|
**Excluded patterns:** `__pycache__`, `.pyc`, `.git/`, `node_modules`, `.DS_Store`, `.swp`, temp files ending in `~`
|
|
209
292
|
|
|
210
|
-
**Git commits** are detected
|
|
293
|
+
**Git commits** are detected automatically — only commits in a git repo rooted at (or above) the watch directory are captured.
|
|
211
294
|
|
|
212
295
|
## Data Directory
|
|
213
296
|
|
|
@@ -64,7 +64,8 @@ def main() -> None:
|
|
|
64
64
|
threads.append(threading.Thread(
|
|
65
65
|
target=bridge.start,
|
|
66
66
|
args=(sid, stop_event, 9877,
|
|
67
|
-
cfg.get("token", ""), cfg.get("api_url", ""), cfg.get("e2e_key", "")
|
|
67
|
+
cfg.get("token", ""), cfg.get("api_url", ""), cfg.get("e2e_key", ""),
|
|
68
|
+
cfg.get("journal_mode", False)),
|
|
68
69
|
daemon=True,
|
|
69
70
|
))
|
|
70
71
|
|
|
@@ -145,17 +145,17 @@ def emit(event_type: str, metadata: dict[str, Any]) -> None:
|
|
|
145
145
|
if _e2e_key:
|
|
146
146
|
from methodproof.crypto import encrypt_metadata
|
|
147
147
|
entry["metadata"] = encrypt_metadata(dict(entry["metadata"]), _e2e_key)
|
|
148
|
-
global _prev_hash
|
|
149
|
-
from methodproof.integrity import compute_event_hash
|
|
150
|
-
entry["_chain_hash"] = compute_event_hash(entry, _prev_hash, _account_id)
|
|
151
|
-
_prev_hash = entry["_chain_hash"]
|
|
152
|
-
if _live_mode:
|
|
153
|
-
from methodproof import live as live_mod
|
|
154
|
-
live_mod.send(entry)
|
|
155
148
|
with _lock:
|
|
149
|
+
global _prev_hash
|
|
150
|
+
from methodproof.integrity import compute_event_hash
|
|
151
|
+
entry["_chain_hash"] = compute_event_hash(entry, _prev_hash, _account_id)
|
|
152
|
+
_prev_hash = entry["_chain_hash"]
|
|
156
153
|
_buffer.append(entry)
|
|
157
154
|
if len(_buffer) >= _FLUSH_SIZE:
|
|
158
155
|
_flush_locked()
|
|
156
|
+
if _live_mode:
|
|
157
|
+
from methodproof import live as live_mod
|
|
158
|
+
live_mod.send(entry)
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
def flush() -> None:
|
|
@@ -12,7 +12,8 @@ _session_id = ""
|
|
|
12
12
|
_api_token = ""
|
|
13
13
|
_api_base = ""
|
|
14
14
|
_e2e_key = ""
|
|
15
|
-
|
|
15
|
+
_journal = False
|
|
16
|
+
_pairing: dict[str, Any] = {} # {token: {session_id, api_token, api_base, e2e_key, journal, paired}}
|
|
16
17
|
_extension_paired = threading.Event()
|
|
17
18
|
MAX_BODY = 10 * 1024 * 1024 # 10 MB
|
|
18
19
|
|
|
@@ -47,6 +48,7 @@ PAIR_PAGE = """<!DOCTYPE html>
|
|
|
47
48
|
data-token="{api_token}"
|
|
48
49
|
data-api-base="{api_base}"
|
|
49
50
|
data-e2e-key="{e2e_key}"
|
|
51
|
+
data-journal="{journal}"
|
|
50
52
|
style="display:none"></div>
|
|
51
53
|
</div>
|
|
52
54
|
<script>
|
|
@@ -62,11 +64,12 @@ PAIR_PAGE = """<!DOCTYPE html>
|
|
|
62
64
|
</html>"""
|
|
63
65
|
|
|
64
66
|
|
|
65
|
-
def generate_pair_token(session_id: str, api_token: str, api_base: str,
|
|
67
|
+
def generate_pair_token(session_id: str, api_token: str, api_base: str,
|
|
68
|
+
e2e_key: str = "", journal: bool = False) -> str:
|
|
66
69
|
token = secrets.token_urlsafe(16)
|
|
67
70
|
_pairing[token] = {
|
|
68
71
|
"session_id": session_id, "api_token": api_token,
|
|
69
|
-
"api_base": api_base, "e2e_key": e2e_key, "paired": False,
|
|
72
|
+
"api_base": api_base, "e2e_key": e2e_key, "journal": journal, "paired": False,
|
|
70
73
|
}
|
|
71
74
|
return token
|
|
72
75
|
|
|
@@ -92,6 +95,7 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
92
95
|
api_token=data["api_token"],
|
|
93
96
|
api_base=data["api_base"],
|
|
94
97
|
e2e_key=data["e2e_key"],
|
|
98
|
+
journal="true" if data.get("journal") else "false",
|
|
95
99
|
pair_token=token,
|
|
96
100
|
).encode()
|
|
97
101
|
self.send_response(200)
|
|
@@ -109,6 +113,7 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
109
113
|
"token": _api_token,
|
|
110
114
|
"api_base": _api_base,
|
|
111
115
|
"e2e_key": _e2e_key,
|
|
116
|
+
"journal": _journal,
|
|
112
117
|
})
|
|
113
118
|
_extension_paired.set()
|
|
114
119
|
base.log("info", "extension.auto_paired", session_id=_session_id)
|
|
@@ -149,6 +154,7 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
149
154
|
"api_token": body.get("api_token", ""),
|
|
150
155
|
"api_base": body.get("api_base", ""),
|
|
151
156
|
"e2e_key": body.get("e2e_key", ""),
|
|
157
|
+
"journal": body.get("journal", False),
|
|
152
158
|
"paired": False,
|
|
153
159
|
}
|
|
154
160
|
self._json({"ok": True, "token": token})
|
|
@@ -185,12 +191,14 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
185
191
|
|
|
186
192
|
|
|
187
193
|
def start(session_id: str, stop: threading.Event, port: int = 9877,
|
|
188
|
-
api_token: str = "", api_base: str = "", e2e_key: str = ""
|
|
189
|
-
|
|
194
|
+
api_token: str = "", api_base: str = "", e2e_key: str = "",
|
|
195
|
+
journal: bool = False) -> None:
|
|
196
|
+
global _session_id, _api_token, _api_base, _e2e_key, _journal
|
|
190
197
|
_session_id = session_id
|
|
191
198
|
_api_token = api_token
|
|
192
199
|
_api_base = api_base
|
|
193
200
|
_e2e_key = e2e_key
|
|
201
|
+
_journal = journal
|
|
194
202
|
_extension_paired.clear()
|
|
195
203
|
HTTPServer.allow_reuse_address = True
|
|
196
204
|
server = HTTPServer(("127.0.0.1", port), _Handler)
|
|
@@ -1028,6 +1028,7 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
1028
1028
|
threads.append(threading.Thread(target=bridge.start, args=(
|
|
1029
1029
|
sid, stop_event, 9877,
|
|
1030
1030
|
cfg.get("token", ""), cfg.get("api_url", ""), cfg.get("e2e_key", ""),
|
|
1031
|
+
cfg.get("journal_mode", False),
|
|
1031
1032
|
), daemon=True))
|
|
1032
1033
|
if capture.get("music", True):
|
|
1033
1034
|
from methodproof.agents import music
|
|
@@ -79,7 +79,6 @@ JOURNAL_CONTENT_FIELDS: list[tuple[str, str]] = [
|
|
|
79
79
|
# AI prompts — full prompt text
|
|
80
80
|
("llm_prompt", "prompt_text"),
|
|
81
81
|
("agent_prompt", "prompt_preview"),
|
|
82
|
-
("user_prompt", "prompt_preview"),
|
|
83
82
|
# AI responses — full completion text
|
|
84
83
|
("llm_completion", "response_text"),
|
|
85
84
|
("agent_completion", "response_preview"),
|
|
@@ -105,8 +104,8 @@ JOURNAL_CONTENT_FIELDS: list[tuple[str, str]] = [
|
|
|
105
104
|
("browser_copy", "text_snippet"),
|
|
106
105
|
("browser_ai_chat", "detected_input"),
|
|
107
106
|
("browser_ai_chat", "url"),
|
|
108
|
-
#
|
|
109
|
-
("
|
|
107
|
+
# Tasks — subject reveals intent
|
|
108
|
+
("task_created", "subject"),
|
|
110
109
|
]
|
|
111
110
|
|
|
112
111
|
|
|
@@ -102,9 +102,10 @@ def push(session_id: str, token: str, api_url: str) -> str:
|
|
|
102
102
|
event_hashes = store.get_event_hashes(session_id)
|
|
103
103
|
hash_lookup = {h["event_id"]: h["hash"] for h in event_hashes}
|
|
104
104
|
total = len(events)
|
|
105
|
+
batch_size = 500
|
|
105
106
|
try:
|
|
106
|
-
for i in range(0, total,
|
|
107
|
-
batch = events[i:i +
|
|
107
|
+
for i in range(0, total, batch_size):
|
|
108
|
+
batch = events[i:i + batch_size]
|
|
108
109
|
payload = [{"id": e["id"], "type": e["type"],
|
|
109
110
|
"timestamp": _iso(e["timestamp"]),
|
|
110
111
|
"timestamp_raw": e["timestamp"],
|
|
@@ -112,9 +113,18 @@ def push(session_id: str, token: str, api_url: str) -> str:
|
|
|
112
113
|
"metadata": json.loads(e["metadata"]),
|
|
113
114
|
"hash": hash_lookup.get(e["id"], "")}
|
|
114
115
|
for e in batch]
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
for attempt in range(5):
|
|
117
|
+
try:
|
|
118
|
+
_request("POST", f"/sessions/{remote_id}/events", api_url, token,
|
|
119
|
+
{"events": payload})
|
|
120
|
+
break
|
|
121
|
+
except SystemExit as exc:
|
|
122
|
+
if "429" in str(exc) and attempt < 4:
|
|
123
|
+
import time
|
|
124
|
+
time.sleep(10 * (attempt + 1))
|
|
125
|
+
else:
|
|
126
|
+
raise
|
|
127
|
+
done = min(i + batch_size, total)
|
|
118
128
|
print(f"\r Uploading: {done}/{total} events", end="", flush=True)
|
|
119
129
|
print()
|
|
120
130
|
except SystemExit:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "methodproof"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "See how you code. Capture and visualize your engineering process."
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
dependencies = ["watchdog>=4.0", "websocket-client>=1.7", "cryptography>=43.0", "keyring>=25.0"]
|
|
@@ -649,7 +649,7 @@ name = "importlib-metadata"
|
|
|
649
649
|
version = "9.0.0"
|
|
650
650
|
source = { registry = "https://pypi.org/simple" }
|
|
651
651
|
dependencies = [
|
|
652
|
-
{ name = "zipp" },
|
|
652
|
+
{ name = "zipp", marker = "python_full_version < '3.12'" },
|
|
653
653
|
]
|
|
654
654
|
sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" }
|
|
655
655
|
wheels = [
|
|
@@ -862,7 +862,7 @@ wheels = [
|
|
|
862
862
|
|
|
863
863
|
[[package]]
|
|
864
864
|
name = "methodproof"
|
|
865
|
-
version = "0.
|
|
865
|
+
version = "0.5.5"
|
|
866
866
|
source = { editable = "." }
|
|
867
867
|
dependencies = [
|
|
868
868
|
{ name = "cryptography", version = "44.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|