methodproof 0.6.0__tar.gz → 0.7.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.6.0 → methodproof-0.7.0}/CHANGELOG.md +11 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/PKG-INFO +1 -1
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/_daemon.py +16 -2
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/agents/base.py +55 -3
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/agents/watcher.py +29 -1
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/cli.py +80 -20
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/claude_code.py +47 -1
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/claude_code.sh +52 -2
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/install.py +14 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/pyproject.toml +1 -1
- {methodproof-0.6.0 → methodproof-0.7.0}/uv.lock +1 -1
- {methodproof-0.6.0 → methodproof-0.7.0}/.github/workflows/ci.yml +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/.gitignore +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/LICENSE +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/README.md +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/__init__.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/__main__.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/agents/__init__.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/agents/music.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/agents/terminal.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/analysis.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/binding.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/bip39.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/bridge.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/config.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/crypto.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/graph.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hook.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/__init__.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/cline_hook.sh +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/codex_hook.sh +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/gemini_hook.sh +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/kiro_hook.sh +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/mcp_register.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/openclaw/HOOK.md +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/openclaw/handler.ts +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/openclaw_install.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/opencode_plugin.js +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/hooks/wrappers.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/integrity.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/kdf.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/keychain.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/live.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/lock.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/mcp.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/migrate_db.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/proxy.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/proxy_daemon.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/repos.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/skills/methodproof/SKILL.md +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/store.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/sync.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/viewer.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/methodproof/wordlist.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/test_windows_compat.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/__init__.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_analysis.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_graph.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_hooks.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_live.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_openclaw_hooks.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_security.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_store.py +0 -0
- {methodproof-0.6.0 → methodproof-0.7.0}/tests/test_wrappers.py +0 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0] — 2026-04-07
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Runaway event logging** — watcher ignored only 7 patterns, tracking 35K+ files including `.venv/` (20K), `dist/build/` (23K). Expanded to 30+ ignore patterns covering Python, JS/TS, Rust, Go, Java, Ruby, PHP, .NET, Swift, and build artifacts. **138K → 1,442 files (99% reduction)**
|
|
7
|
+
- **Claude Code hook errors** — hook script pointed to deleted pyenv site-packages path; added pidfile guard (`exit 0` when no session active) to prevent bridge connection failures
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `mp start --streaming` — blocking foreground mode, streams every captured event to stdout in real-time with human-readable formatting
|
|
11
|
+
- `mp start --verbose` / `-v` — structured debug logging at each step (config, auth, session, anchor, daemon spawn); daemon log includes agent init and buffer/flush stats
|
|
12
|
+
- Step-by-step progress output on default `mp start` (`→ Loading config`, `→ Checking hooks`, etc.) so failures are immediately locatable
|
|
13
|
+
|
|
3
14
|
## [0.5.1] — 2026-04-06
|
|
4
15
|
|
|
5
16
|
### Fixed
|
|
@@ -18,6 +18,7 @@ PIDFILE = config.DIR / "methodproof.pid"
|
|
|
18
18
|
def main() -> None:
|
|
19
19
|
sid = sys.argv[1]
|
|
20
20
|
watch_dir = sys.argv[2]
|
|
21
|
+
verbose = "--verbose" in sys.argv
|
|
21
22
|
|
|
22
23
|
cfg = config.load()
|
|
23
24
|
capture = cfg.get("capture", {})
|
|
@@ -28,10 +29,15 @@ def main() -> None:
|
|
|
28
29
|
del cfg["_live_url"]
|
|
29
30
|
config.save(cfg)
|
|
30
31
|
|
|
31
|
-
base.init(sid, live=bool(live_url))
|
|
32
|
+
base.init(sid, live=bool(live_url), verbose=verbose)
|
|
33
|
+
|
|
34
|
+
if verbose:
|
|
35
|
+
active = [k for k, v in capture.items() if v]
|
|
36
|
+
base.log("info", "daemon.config", capture=active, live=bool(live_url),
|
|
37
|
+
journal=cfg.get("journal_mode", False))
|
|
32
38
|
|
|
33
|
-
# Environment profile (was done pre-fork in old code, now done in daemon)
|
|
34
39
|
if capture.get("environment_analysis", True):
|
|
40
|
+
base.log("info", "daemon.agent.environment_scan") if verbose else None
|
|
35
41
|
try:
|
|
36
42
|
from methodproof.analysis import scan_environment
|
|
37
43
|
env_profile = scan_environment(watch_dir)
|
|
@@ -52,12 +58,16 @@ def main() -> None:
|
|
|
52
58
|
threads.append(threading.Thread(
|
|
53
59
|
target=watcher.start, args=(watch_dir, stop_event), daemon=True,
|
|
54
60
|
))
|
|
61
|
+
if verbose:
|
|
62
|
+
base.log("info", "daemon.agent.watcher", dir=watch_dir)
|
|
55
63
|
|
|
56
64
|
if capture.get("terminal_commands", True) or capture.get("test_results", True):
|
|
57
65
|
from methodproof.agents import terminal
|
|
58
66
|
threads.append(threading.Thread(
|
|
59
67
|
target=terminal.start, args=(stop_event,), daemon=True,
|
|
60
68
|
))
|
|
69
|
+
if verbose:
|
|
70
|
+
base.log("info", "daemon.agent.terminal", log=str(config.CMD_LOG))
|
|
61
71
|
|
|
62
72
|
if capture.get("browser", True):
|
|
63
73
|
from methodproof import bridge
|
|
@@ -68,12 +78,16 @@ def main() -> None:
|
|
|
68
78
|
cfg.get("journal_mode", False)),
|
|
69
79
|
daemon=True,
|
|
70
80
|
))
|
|
81
|
+
if verbose:
|
|
82
|
+
base.log("info", "daemon.agent.bridge", port=9877)
|
|
71
83
|
|
|
72
84
|
if capture.get("music", True):
|
|
73
85
|
from methodproof.agents import music
|
|
74
86
|
threads.append(threading.Thread(
|
|
75
87
|
target=music.start, args=(stop_event,), daemon=True,
|
|
76
88
|
))
|
|
89
|
+
if verbose:
|
|
90
|
+
base.log("info", "daemon.agent.music")
|
|
77
91
|
|
|
78
92
|
def _shutdown(sig_num: int, frame: object) -> None:
|
|
79
93
|
stop_event.set()
|
|
@@ -21,6 +21,8 @@ _MAX_RETRIES = 3
|
|
|
21
21
|
_prev_hash = "genesis"
|
|
22
22
|
_account_id = ""
|
|
23
23
|
_journal_mode = False
|
|
24
|
+
_verbose = False
|
|
25
|
+
_streaming = False
|
|
24
26
|
|
|
25
27
|
# Maps event types to the capture category that gates them
|
|
26
28
|
_EVENT_GATES: dict[str, str] = {
|
|
@@ -91,11 +93,13 @@ def _load_encryption_key(cfg: dict) -> bytes | None:
|
|
|
91
93
|
return bytes.fromhex(raw) if raw else None
|
|
92
94
|
|
|
93
95
|
|
|
94
|
-
def init(session_id: str, live: bool = False) -> None:
|
|
95
|
-
global _session_id, _initialized, _e2e_key, _capture, _live_mode, _prev_hash, _journal_mode, _account_id
|
|
96
|
+
def init(session_id: str, live: bool = False, verbose: bool = False, streaming: bool = False) -> None:
|
|
97
|
+
global _session_id, _initialized, _e2e_key, _capture, _live_mode, _prev_hash, _journal_mode, _account_id, _verbose, _streaming
|
|
96
98
|
_session_id = session_id
|
|
97
99
|
_initialized = True
|
|
98
100
|
_live_mode = live
|
|
101
|
+
_verbose = verbose
|
|
102
|
+
_streaming = streaming
|
|
99
103
|
_prev_hash = "genesis"
|
|
100
104
|
from methodproof import config
|
|
101
105
|
cfg = config.load()
|
|
@@ -103,6 +107,10 @@ def init(session_id: str, live: bool = False) -> None:
|
|
|
103
107
|
_capture = cfg.get("capture", {})
|
|
104
108
|
_journal_mode = cfg.get("journal_mode", False)
|
|
105
109
|
_account_id = cfg.get("account_id", "")
|
|
110
|
+
if _verbose or _streaming:
|
|
111
|
+
active = [k for k, v in _capture.items() if v]
|
|
112
|
+
log("info", "base.init", encryption=bool(_e2e_key), journal=_journal_mode,
|
|
113
|
+
live=_live_mode, capture=active)
|
|
106
114
|
|
|
107
115
|
|
|
108
116
|
def log(level: str, event: str, **kw: object) -> None:
|
|
@@ -153,6 +161,10 @@ def emit(event_type: str, metadata: dict[str, Any]) -> None:
|
|
|
153
161
|
_buffer.append(entry)
|
|
154
162
|
if len(_buffer) >= _FLUSH_SIZE:
|
|
155
163
|
_flush_locked()
|
|
164
|
+
if _verbose:
|
|
165
|
+
log("debug", "emit.buffered", type=event_type, buffer=len(_buffer))
|
|
166
|
+
if _streaming:
|
|
167
|
+
_stream_event(entry)
|
|
156
168
|
if _live_mode:
|
|
157
169
|
from methodproof import live as live_mod
|
|
158
170
|
live_mod.send(entry)
|
|
@@ -168,15 +180,55 @@ def _flush_locked() -> None:
|
|
|
168
180
|
return
|
|
169
181
|
batch = list(_buffer)
|
|
170
182
|
hashes = [(e["id"], e.pop("_chain_hash")) for e in batch if "_chain_hash" in e]
|
|
183
|
+
if _verbose or _streaming:
|
|
184
|
+
types = [e["type"] for e in batch]
|
|
185
|
+
log("info", "flush.start", count=len(batch), types=types)
|
|
171
186
|
for attempt in range(_MAX_RETRIES):
|
|
172
187
|
try:
|
|
173
188
|
store.insert_events(_session_id, batch)
|
|
174
189
|
if hashes:
|
|
175
190
|
store.insert_event_hashes(hashes)
|
|
191
|
+
if _verbose or _streaming:
|
|
192
|
+
log("info", "flush.ok", count=len(batch))
|
|
176
193
|
_buffer.clear()
|
|
177
194
|
return
|
|
178
195
|
except Exception as exc:
|
|
179
196
|
log("warning", "flush.retry", attempt=attempt + 1, error=str(exc))
|
|
180
197
|
time.sleep(0.1 * (attempt + 1))
|
|
181
|
-
# Final attempt failed — keep events in buffer for next flush cycle
|
|
182
198
|
log("error", "flush.failed", count=len(batch), retries=_MAX_RETRIES)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _stream_event(entry: dict[str, Any]) -> None:
|
|
202
|
+
"""Print a human-readable event line to stdout for --streaming mode."""
|
|
203
|
+
ts = time.strftime("%H:%M:%S", time.localtime(entry["timestamp"]))
|
|
204
|
+
etype = entry["type"]
|
|
205
|
+
meta = entry.get("metadata", {})
|
|
206
|
+
# Build a compact summary per event type
|
|
207
|
+
detail = ""
|
|
208
|
+
if etype == "file_edit":
|
|
209
|
+
detail = f'{meta.get("path", "?")} +{meta.get("lines_added", 0)}-{meta.get("lines_removed", 0)}'
|
|
210
|
+
elif etype == "file_create":
|
|
211
|
+
detail = f'{meta.get("path", "?")} ({meta.get("size", 0)}B)'
|
|
212
|
+
elif etype == "file_delete":
|
|
213
|
+
detail = meta.get("path", "?")
|
|
214
|
+
elif etype == "terminal_cmd":
|
|
215
|
+
detail = f'{meta.get("command", "?")[:60]} → exit {meta.get("exit_code", "?")}'
|
|
216
|
+
elif etype == "test_run":
|
|
217
|
+
detail = f'{meta.get("framework", "?")} {meta.get("passed", 0)}✓ {meta.get("failed", 0)}✗'
|
|
218
|
+
elif etype == "git_commit":
|
|
219
|
+
detail = f'{meta.get("hash", "?")} {meta.get("message", "")[:50]}'
|
|
220
|
+
elif etype in ("llm_prompt", "agent_prompt", "user_prompt"):
|
|
221
|
+
detail = f'len={meta.get("prompt_length", meta.get("message_length", "?"))}'
|
|
222
|
+
elif etype in ("llm_completion", "agent_completion"):
|
|
223
|
+
detail = f'len={meta.get("response_length", "?")}'
|
|
224
|
+
elif etype == "music_playing":
|
|
225
|
+
detail = f'{meta.get("artist", "?")} — {meta.get("track", "?")}'
|
|
226
|
+
elif etype.startswith("browser_"):
|
|
227
|
+
detail = meta.get("url", meta.get("query", ""))[:60]
|
|
228
|
+
elif etype == "environment_profile":
|
|
229
|
+
detail = f'{meta.get("tool_count", "?")} tools'
|
|
230
|
+
else:
|
|
231
|
+
keys = list(meta.keys())[:4]
|
|
232
|
+
detail = ", ".join(f"{k}={meta[k]}" for k in keys) if keys else ""
|
|
233
|
+
sys.stdout.write(f" [{ts}] {etype:30s} {detail}\n")
|
|
234
|
+
sys.stdout.flush()
|
|
@@ -13,7 +13,35 @@ from watchdog.observers import Observer
|
|
|
13
13
|
from methodproof.agents import base
|
|
14
14
|
|
|
15
15
|
IGNORE_PATTERNS = re.compile(
|
|
16
|
-
|
|
16
|
+
# Version control
|
|
17
|
+
r"(\.git/|\.hg/|\.svn/"
|
|
18
|
+
# OS / editor artifacts
|
|
19
|
+
r"|\.DS_Store|Thumbs\.db|\.swp|\.swo|~$|\.idea/|\.vscode/"
|
|
20
|
+
# Python
|
|
21
|
+
r"|__pycache__|\.pyc|\.pyo|\.egg-info/|\.eggs/|\.tox/"
|
|
22
|
+
r"|\.venv/|venv/|\.env/|env/|\.mypy_cache/|\.pytest_cache/|\.ruff_cache/"
|
|
23
|
+
r"|\.coverage|htmlcov/|\.nox/"
|
|
24
|
+
# JavaScript / TypeScript
|
|
25
|
+
r"|node_modules/|\.next/|\.nuxt/|\.expo/|\.turbo/|\.parcel-cache/"
|
|
26
|
+
r"|\.svelte-kit/|\.angular/|\.cache/"
|
|
27
|
+
# Rust
|
|
28
|
+
r"|/target/|\.cargo/"
|
|
29
|
+
# Go
|
|
30
|
+
r"|vendor/"
|
|
31
|
+
# Java / Kotlin / JVM
|
|
32
|
+
r"|\.gradle/|\.m2/|/out/"
|
|
33
|
+
# Ruby
|
|
34
|
+
r"|\.bundle/|\.gem/"
|
|
35
|
+
# PHP
|
|
36
|
+
r"|/vendor/|\.phpunit/"
|
|
37
|
+
# .NET / C#
|
|
38
|
+
r"|/bin/|/obj/|\.nuget/"
|
|
39
|
+
# Swift / Xcode
|
|
40
|
+
r"|\.build/|DerivedData/|Pods/"
|
|
41
|
+
# Build output / artifacts
|
|
42
|
+
r"|dist/|build/|\.output/"
|
|
43
|
+
# Logs and locks
|
|
44
|
+
r"|\.lock$|\.log$)"
|
|
17
45
|
)
|
|
18
46
|
|
|
19
47
|
|
|
@@ -845,28 +845,54 @@ def _is_daemon_alive() -> bool:
|
|
|
845
845
|
return False
|
|
846
846
|
|
|
847
847
|
|
|
848
|
+
def _log_step(msg: str, verbose: bool = False) -> None:
|
|
849
|
+
"""Print a step-by-step progress line. Always shown."""
|
|
850
|
+
print(f" → {msg}")
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def _log_debug(msg: str, **kw: object) -> None:
|
|
854
|
+
"""Print structured debug line (--verbose only). Writes to stderr."""
|
|
855
|
+
import json as _json
|
|
856
|
+
entry = {"ts": time.time(), "level": "debug", "event": msg, **kw}
|
|
857
|
+
sys.stderr.write(_json.dumps(entry, default=str) + "\n")
|
|
858
|
+
|
|
859
|
+
|
|
848
860
|
def cmd_start(args: argparse.Namespace) -> None:
|
|
861
|
+
verbose = getattr(args, "verbose", False)
|
|
862
|
+
streaming = getattr(args, "streaming", False)
|
|
863
|
+
|
|
864
|
+
_log_step("Loading config")
|
|
849
865
|
cfg = config.load()
|
|
866
|
+
if verbose:
|
|
867
|
+
_log_debug("config.loaded", api_url=cfg.get("api_url"), account_id=cfg.get("account_id", "")[:8],
|
|
868
|
+
active_session=cfg.get("active_session"), journal=cfg.get("journal_mode"))
|
|
869
|
+
|
|
850
870
|
if cfg.get("auto_update"):
|
|
871
|
+
_log_step("Checking for updates")
|
|
851
872
|
_auto_update()
|
|
873
|
+
|
|
852
874
|
if cfg.get("active_session"):
|
|
853
875
|
if _is_daemon_alive():
|
|
854
876
|
print(f"Session active: {cfg['active_session'][:8]}")
|
|
855
877
|
print("Run `methodproof stop` first.")
|
|
856
878
|
sys.exit(1)
|
|
857
|
-
# Daemon is dead — clean up the stale session
|
|
858
879
|
stale_sid = cfg["active_session"]
|
|
880
|
+
_log_step(f"Cleaning stale session {stale_sid[:8]}")
|
|
859
881
|
store.complete_session(stale_sid)
|
|
860
882
|
graph.build(stale_sid)
|
|
861
883
|
PIDFILE.unlink(missing_ok=True)
|
|
862
884
|
cfg["active_session"] = None
|
|
863
885
|
config.save(cfg)
|
|
864
|
-
|
|
886
|
+
|
|
887
|
+
_log_step("Checking hooks")
|
|
865
888
|
if not hook.is_installed():
|
|
866
|
-
print("Run `methodproof init` first.")
|
|
889
|
+
print("ERROR: Run `methodproof init` first.")
|
|
867
890
|
sys.exit(1)
|
|
868
891
|
|
|
892
|
+
_log_step("Authenticating")
|
|
869
893
|
account_id = _require_auth(cfg)
|
|
894
|
+
if verbose:
|
|
895
|
+
_log_debug("auth.ok", account_id=account_id[:8] if account_id else "none")
|
|
870
896
|
|
|
871
897
|
# Check for new consent categories before recording
|
|
872
898
|
capture = cfg.get("capture", {})
|
|
@@ -878,6 +904,7 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
878
904
|
config.save(cfg)
|
|
879
905
|
print()
|
|
880
906
|
|
|
907
|
+
_log_step("Creating session")
|
|
881
908
|
sid = uuid.uuid4().hex
|
|
882
909
|
watch_dir = os.path.abspath(args.dir or ".")
|
|
883
910
|
repo_url = args.repo or repos.detect_repo(watch_dir)
|
|
@@ -885,7 +912,6 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
885
912
|
visibility = "public" if args.public else "private"
|
|
886
913
|
from methodproof.binding import compute_binding, compute_device_id
|
|
887
914
|
device_id = compute_device_id()
|
|
888
|
-
# Compute session binding if master key is available
|
|
889
915
|
binding = ""
|
|
890
916
|
if cfg.get("master_key_fingerprint") and account_id:
|
|
891
917
|
from methodproof.keychain import load_secret
|
|
@@ -895,38 +921,45 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
895
921
|
master = derive_master(entropy)
|
|
896
922
|
bind_key = derive_bind_key(master, account_id)
|
|
897
923
|
binding = compute_binding(bind_key, sid, account_id, device_id, time.time())
|
|
924
|
+
if verbose:
|
|
925
|
+
_log_debug("binding.computed", device_id=device_id[:8])
|
|
898
926
|
store.create_session(sid, watch_dir, repo_url, json.dumps(tags), visibility,
|
|
899
927
|
account_id, binding, device_id)
|
|
900
928
|
cfg["active_session"] = sid
|
|
901
929
|
config.save(cfg)
|
|
902
930
|
PIDFILE.write_text(str(os.getpid()))
|
|
931
|
+
if verbose:
|
|
932
|
+
_log_debug("session.created", sid=sid[:8], watch_dir=watch_dir, visibility=visibility,
|
|
933
|
+
device_id=device_id[:8], bound=bool(binding))
|
|
903
934
|
|
|
904
|
-
# Temporal anchor
|
|
935
|
+
# Temporal anchor
|
|
905
936
|
if cfg.get("token"):
|
|
937
|
+
_log_step("Requesting temporal anchor")
|
|
906
938
|
try:
|
|
907
939
|
from methodproof.sync import _request
|
|
908
940
|
anchor = _request("POST", f"/sessions/{sid}/anchor", cfg["api_url"], cfg["token"])
|
|
909
941
|
store.update_anchor(sid, anchor["anchor_ts"], anchor["signature"])
|
|
942
|
+
if verbose:
|
|
943
|
+
_log_debug("anchor.ok", anchor_ts=anchor["anchor_ts"])
|
|
910
944
|
except Exception as exc:
|
|
911
|
-
|
|
945
|
+
_log_step(f"Anchor: skipped ({exc})")
|
|
912
946
|
|
|
913
947
|
from methodproof.agents import base
|
|
914
|
-
live_ok = False
|
|
915
948
|
capture = cfg.get("capture", {})
|
|
916
949
|
|
|
950
|
+
# Live streaming
|
|
917
951
|
live_url = ""
|
|
918
952
|
want_live = args.live or getattr(args, "live_public", False)
|
|
919
953
|
live_visibility = "public" if getattr(args, "live_public", False) else "private"
|
|
920
954
|
if want_live:
|
|
955
|
+
_log_step("Connecting live stream")
|
|
921
956
|
if not cfg.get("token"):
|
|
922
957
|
print("Live mode requires login. Run `methodproof login` first.")
|
|
923
958
|
sys.exit(1)
|
|
924
|
-
# Create remote session first
|
|
925
959
|
from methodproof.sync import _request
|
|
926
960
|
result = _request("POST", "/personal/sessions", cfg["api_url"], cfg["token"])
|
|
927
961
|
remote_id = result["session_id"]
|
|
928
962
|
store.mark_synced(sid, remote_id)
|
|
929
|
-
# Connect live WebSocket
|
|
930
963
|
from methodproof import live as live_mod
|
|
931
964
|
live_url = live_mod.start(cfg["api_url"], cfg["token"], remote_id, capture, live_visibility) or ""
|
|
932
965
|
if not live_url:
|
|
@@ -939,7 +972,7 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
939
972
|
print(f"Live (public): {live_url}")
|
|
940
973
|
print(" Anyone with this link can watch your session build in real time.")
|
|
941
974
|
|
|
942
|
-
# Journal mode
|
|
975
|
+
# Journal mode
|
|
943
976
|
if getattr(args, "journal", False):
|
|
944
977
|
cfg["journal_mode"] = True
|
|
945
978
|
credits = cfg.get("journal_credits", 0)
|
|
@@ -966,25 +999,35 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
966
999
|
print(f"Bridge: http://localhost:9877")
|
|
967
1000
|
if live_url:
|
|
968
1001
|
print(f"Live: {live_url}")
|
|
1002
|
+
if verbose:
|
|
1003
|
+
print(f"Mode: verbose (debug → daemon.log)")
|
|
1004
|
+
if streaming:
|
|
1005
|
+
print(f"Mode: streaming (blocking, events → stdout)")
|
|
1006
|
+
|
|
1007
|
+
# --streaming: blocking foreground mode with real-time event output
|
|
1008
|
+
if streaming:
|
|
1009
|
+
_run_foreground(sid, watch_dir, cfg, capture, live_url, verbose=True, streaming=True)
|
|
1010
|
+
return
|
|
969
1011
|
|
|
970
|
-
# Daemonize
|
|
1012
|
+
# Daemonize (macOS/Linux) — pass --verbose to daemon if set
|
|
971
1013
|
if sys.platform != "win32":
|
|
972
1014
|
import subprocess as _sp
|
|
973
1015
|
daemon_log = config.DIR / "daemon.log"
|
|
974
1016
|
log_fh = open(daemon_log, "a")
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
)
|
|
1017
|
+
cmd = [sys.executable, "-m", "methodproof._daemon", sid, watch_dir]
|
|
1018
|
+
if verbose:
|
|
1019
|
+
cmd.append("--verbose")
|
|
1020
|
+
_log_step(f"Spawning daemon (log: {daemon_log})")
|
|
1021
|
+
proc = _sp.Popen(cmd, start_new_session=True, stdout=log_fh, stderr=log_fh, stdin=_sp.DEVNULL)
|
|
980
1022
|
PIDFILE.write_text(str(proc.pid))
|
|
981
|
-
|
|
1023
|
+
if verbose:
|
|
1024
|
+
_log_debug("daemon.spawned", pid=proc.pid, cmd=cmd)
|
|
982
1025
|
time.sleep(1)
|
|
983
1026
|
if proc.poll() is not None:
|
|
984
1027
|
print(f"ERROR: Daemon exited immediately (code {proc.returncode}). Check {daemon_log}")
|
|
985
1028
|
PIDFILE.unlink(missing_ok=True)
|
|
986
1029
|
sys.exit(1)
|
|
987
|
-
|
|
1030
|
+
_log_step(f"Daemon alive (pid {proc.pid})")
|
|
988
1031
|
if capture.get("browser", True):
|
|
989
1032
|
import urllib.request as _ur
|
|
990
1033
|
time.sleep(7)
|
|
@@ -1001,9 +1044,16 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
1001
1044
|
return
|
|
1002
1045
|
|
|
1003
1046
|
# Windows: foreground mode (no subprocess daemonization)
|
|
1047
|
+
_run_foreground(sid, watch_dir, cfg, capture, live_url, verbose=verbose, streaming=False)
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
def _run_foreground(sid: str, watch_dir: str, cfg: dict, capture: dict,
|
|
1051
|
+
live_url: str, verbose: bool, streaming: bool) -> None:
|
|
1052
|
+
"""Run capture agents in the foreground (blocking). Used by --streaming and Windows."""
|
|
1004
1053
|
from methodproof.agents import base as _base
|
|
1005
|
-
_base.init(sid, live=bool(live_url))
|
|
1054
|
+
_base.init(sid, live=bool(live_url), verbose=verbose, streaming=streaming)
|
|
1006
1055
|
if capture.get("environment_analysis", True):
|
|
1056
|
+
_log_step("Scanning environment")
|
|
1007
1057
|
try:
|
|
1008
1058
|
from methodproof.analysis import scan_environment
|
|
1009
1059
|
env_profile = scan_environment(watch_dir)
|
|
@@ -1020,9 +1070,11 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
1020
1070
|
if files_enabled:
|
|
1021
1071
|
from methodproof.agents import watcher
|
|
1022
1072
|
threads.append(threading.Thread(target=watcher.start, args=(watch_dir, stop_event), daemon=True))
|
|
1073
|
+
_log_step("Agent: watcher")
|
|
1023
1074
|
if capture.get("terminal_commands", True) or capture.get("test_results", True):
|
|
1024
1075
|
from methodproof.agents import terminal
|
|
1025
1076
|
threads.append(threading.Thread(target=terminal.start, args=(stop_event,), daemon=True))
|
|
1077
|
+
_log_step("Agent: terminal")
|
|
1026
1078
|
if capture.get("browser", True):
|
|
1027
1079
|
from methodproof import bridge
|
|
1028
1080
|
threads.append(threading.Thread(target=bridge.start, args=(
|
|
@@ -1030,9 +1082,11 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
1030
1082
|
cfg.get("token", ""), cfg.get("api_url", ""), cfg.get("e2e_key", ""),
|
|
1031
1083
|
cfg.get("journal_mode", False),
|
|
1032
1084
|
), daemon=True))
|
|
1085
|
+
_log_step("Agent: bridge")
|
|
1033
1086
|
if capture.get("music", True):
|
|
1034
1087
|
from methodproof.agents import music
|
|
1035
1088
|
threads.append(threading.Thread(target=music.start, args=(stop_event,), daemon=True))
|
|
1089
|
+
_log_step("Agent: music")
|
|
1036
1090
|
|
|
1037
1091
|
def _shutdown(sig: int, frame: object) -> None:
|
|
1038
1092
|
stop_event.set()
|
|
@@ -1054,10 +1108,14 @@ def cmd_start(args: argparse.Namespace) -> None:
|
|
|
1054
1108
|
PIDFILE.unlink(missing_ok=True)
|
|
1055
1109
|
sys.exit(0)
|
|
1056
1110
|
|
|
1111
|
+
_log_step(f"Starting {len(threads)} agent(s)")
|
|
1057
1112
|
for t in threads:
|
|
1058
1113
|
t.start()
|
|
1059
1114
|
signal.signal(signal.SIGINT, _shutdown)
|
|
1060
|
-
|
|
1115
|
+
if streaming:
|
|
1116
|
+
print("\n Streaming events (Ctrl+C to stop):\n")
|
|
1117
|
+
else:
|
|
1118
|
+
print("Press Ctrl+C or run `mp stop` to finish.")
|
|
1061
1119
|
stopfile = config.DIR / "methodproof.stop"
|
|
1062
1120
|
while not stop_event.is_set():
|
|
1063
1121
|
if stopfile.exists():
|
|
@@ -1583,6 +1641,8 @@ def main() -> None:
|
|
|
1583
1641
|
s.add_argument("--live", action="store_true", help="Stream graph live to your private profile")
|
|
1584
1642
|
s.add_argument("--live-public", action="store_true", help="Stream graph live — visible to anyone with the link")
|
|
1585
1643
|
s.add_argument("--journal", action="store_true", help="Journal mode — full content capture (2 free credits, then Pro)")
|
|
1644
|
+
s.add_argument("--verbose", "-v", action="store_true", help="Debug logging at each step (still daemonizes)")
|
|
1645
|
+
s.add_argument("--streaming", action="store_true", help="Blocking foreground — stream every captured event to stdout")
|
|
1586
1646
|
sub.add_parser("stop", help="Stop recording")
|
|
1587
1647
|
v = sub.add_parser("view", help="Inspect captured session data")
|
|
1588
1648
|
v.add_argument("session_id", nargs="?")
|
|
@@ -24,14 +24,36 @@ def _build_prompt_meta(text: str) -> dict:
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
_TYPE_MAP = {
|
|
27
|
+
# Human-initiated
|
|
27
28
|
"UserPromptSubmit": "user_prompt",
|
|
29
|
+
# Tool lifecycle
|
|
28
30
|
"PreToolUse": "tool_call",
|
|
29
31
|
"PostToolUse": "tool_result",
|
|
32
|
+
"PostToolUseFailure": "tool_failure",
|
|
33
|
+
# Agent lifecycle
|
|
30
34
|
"SubagentStart": "agent_launch",
|
|
31
35
|
"SubagentStop": "agent_complete",
|
|
36
|
+
# Task lifecycle
|
|
32
37
|
"TaskCreated": "task_start",
|
|
33
38
|
"TaskCompleted": "task_end",
|
|
39
|
+
# Session lifecycle
|
|
34
40
|
"SessionStart": "claude_session_start",
|
|
41
|
+
"SessionEnd": "claude_session_end",
|
|
42
|
+
"Stop": "agent_turn_end",
|
|
43
|
+
"StopFailure": "agent_turn_error",
|
|
44
|
+
# Context
|
|
45
|
+
"CwdChanged": "cwd_changed",
|
|
46
|
+
"PreCompact": "context_compact_start",
|
|
47
|
+
"PostCompact": "context_compact_end",
|
|
48
|
+
# Permissions
|
|
49
|
+
"PermissionRequest": "permission_request",
|
|
50
|
+
"PermissionDenied": "permission_denied",
|
|
51
|
+
# MCP
|
|
52
|
+
"Elicitation": "mcp_elicitation",
|
|
53
|
+
"ElicitationResult": "mcp_elicitation_result",
|
|
54
|
+
# Worktree
|
|
55
|
+
"WorktreeCreate": "worktree_create",
|
|
56
|
+
"WorktreeRemove": "worktree_remove",
|
|
35
57
|
}
|
|
36
58
|
|
|
37
59
|
_TOOL = "claude_code"
|
|
@@ -43,11 +65,35 @@ _META_EXTRACTORS = {
|
|
|
43
65
|
},
|
|
44
66
|
"PreToolUse": lambda d: {"tool": _TOOL, "tool_name": d.get("tool_name", "unknown")},
|
|
45
67
|
"PostToolUse": lambda d: {"tool": _TOOL, "tool_name": d.get("tool_name", "unknown"), "success": True},
|
|
68
|
+
"PostToolUseFailure": lambda d: {
|
|
69
|
+
"tool": _TOOL, "tool_name": d.get("tool_name", "unknown"),
|
|
70
|
+
"success": False, "is_interrupt": d.get("is_interrupt", False),
|
|
71
|
+
"error": str(d.get("error", ""))[:200],
|
|
72
|
+
},
|
|
46
73
|
"SubagentStart": lambda d: {"tool": _TOOL, "agent_type": d.get("agent_type", "unknown"), "agent_id": d.get("agent_id", "")},
|
|
47
|
-
"SubagentStop": lambda d: {
|
|
74
|
+
"SubagentStop": lambda d: {
|
|
75
|
+
"tool": _TOOL, "agent_type": d.get("agent_type", "unknown"), "agent_id": d.get("agent_id", ""),
|
|
76
|
+
"last_message_preview": str(d.get("last_assistant_message", ""))[:200],
|
|
77
|
+
},
|
|
48
78
|
"TaskCreated": lambda d: {"tool": _TOOL, "task_id": d.get("task_id", ""), "subject": d.get("task_subject", "")},
|
|
49
79
|
"TaskCompleted": lambda d: {"tool": _TOOL, "task_id": d.get("task_id", "")},
|
|
50
80
|
"SessionStart": lambda d: {"tool": _TOOL, "session_id": d.get("session_id", ""), "cwd": d.get("cwd", "")},
|
|
81
|
+
"SessionEnd": lambda d: {"tool": _TOOL, "session_id": d.get("session_id", "")},
|
|
82
|
+
"Stop": lambda d: {"tool": _TOOL},
|
|
83
|
+
"StopFailure": lambda d: {"tool": _TOOL, "error": str(d.get("error", ""))[:200]},
|
|
84
|
+
"CwdChanged": lambda d: {
|
|
85
|
+
"tool": _TOOL, "cwd": d.get("cwd", ""),
|
|
86
|
+
# NOTE: fires for both human `cd` and Claude tool use — caller is ambiguous
|
|
87
|
+
"source": "ambiguous",
|
|
88
|
+
},
|
|
89
|
+
"PreCompact": lambda d: {"tool": _TOOL},
|
|
90
|
+
"PostCompact": lambda d: {"tool": _TOOL},
|
|
91
|
+
"PermissionRequest": lambda d: {"tool": _TOOL, "tool_name": d.get("tool_name", "unknown")},
|
|
92
|
+
"PermissionDenied": lambda d: {"tool": _TOOL, "tool_name": d.get("tool_name", "unknown")},
|
|
93
|
+
"Elicitation": lambda d: {"tool": _TOOL},
|
|
94
|
+
"ElicitationResult": lambda d: {"tool": _TOOL},
|
|
95
|
+
"WorktreeCreate": lambda d: {"tool": _TOOL, "worktree_path": d.get("worktree_path", "")},
|
|
96
|
+
"WorktreeRemove": lambda d: {"tool": _TOOL, "worktree_path": d.get("worktree_path", "")},
|
|
51
97
|
}
|
|
52
98
|
|
|
53
99
|
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
# Receives JSON on stdin. Posts to local bridge. Fails silently.
|
|
4
4
|
# Must complete in <1s to avoid blocking Claude Code.
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Skip if no session is running (no pidfile = no daemon = no bridge)
|
|
7
|
+
[ -f "${HOME}/.methodproof/methodproof.pid" ] || exit 0
|
|
8
|
+
|
|
7
9
|
INPUT=$(cat)
|
|
8
10
|
|
|
9
11
|
if command -v jq >/dev/null 2>&1; then
|
|
@@ -47,7 +49,7 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
47
49
|
;;
|
|
48
50
|
SubagentStop)
|
|
49
51
|
TYPE="agent_complete"
|
|
50
|
-
META=$(echo "$INPUT" | jq -c '{agent_type: (.agent_type // "unknown"), agent_id: (.agent_id // "")}' 2>/dev/null || echo '{}')
|
|
52
|
+
META=$(echo "$INPUT" | jq -c '{agent_type: (.agent_type // "unknown"), agent_id: (.agent_id // ""), last_message_preview: (.last_assistant_message // "" | .[0:200])}' 2>/dev/null || echo '{}')
|
|
51
53
|
;;
|
|
52
54
|
TaskCreated)
|
|
53
55
|
TYPE="task_created"
|
|
@@ -61,6 +63,54 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
61
63
|
TYPE="claude_session_start"
|
|
62
64
|
META=$(echo "$INPUT" | jq -c '{claude_session_id: (.session_id // ""), cwd: (.cwd // "")}' 2>/dev/null || echo '{}')
|
|
63
65
|
;;
|
|
66
|
+
PostToolUseFailure)
|
|
67
|
+
TYPE="tool_failure"
|
|
68
|
+
META=$(echo "$INPUT" | jq -c '{tool_name: (.tool_name // "unknown"), is_interrupt: (.is_interrupt // false), error: (.error // "" | .[0:200])}' 2>/dev/null || echo '{}')
|
|
69
|
+
;;
|
|
70
|
+
SessionEnd)
|
|
71
|
+
TYPE="claude_session_end"
|
|
72
|
+
META=$(echo "$INPUT" | jq -c '{claude_session_id: (.session_id // "")}' 2>/dev/null || echo '{}')
|
|
73
|
+
;;
|
|
74
|
+
Stop)
|
|
75
|
+
TYPE="agent_turn_end"
|
|
76
|
+
META='{"tool":"claude_code"}'
|
|
77
|
+
;;
|
|
78
|
+
StopFailure)
|
|
79
|
+
TYPE="agent_turn_error"
|
|
80
|
+
META=$(echo "$INPUT" | jq -c '{error: (.error // "" | .[0:200])}' 2>/dev/null || echo '{}')
|
|
81
|
+
;;
|
|
82
|
+
CwdChanged)
|
|
83
|
+
TYPE="cwd_changed"
|
|
84
|
+
META=$(echo "$INPUT" | jq -c '{cwd: (.cwd // ""), source: "ambiguous"}' 2>/dev/null || echo '{}')
|
|
85
|
+
;;
|
|
86
|
+
PreCompact)
|
|
87
|
+
TYPE="context_compact_start"
|
|
88
|
+
META='{"tool":"claude_code"}'
|
|
89
|
+
;;
|
|
90
|
+
PostCompact)
|
|
91
|
+
TYPE="context_compact_end"
|
|
92
|
+
META='{"tool":"claude_code"}'
|
|
93
|
+
;;
|
|
94
|
+
PermissionRequest)
|
|
95
|
+
TYPE="permission_request"
|
|
96
|
+
META=$(echo "$INPUT" | jq -c '{tool_name: (.tool_name // "unknown")}' 2>/dev/null || echo '{}')
|
|
97
|
+
;;
|
|
98
|
+
PermissionDenied)
|
|
99
|
+
TYPE="permission_denied"
|
|
100
|
+
META=$(echo "$INPUT" | jq -c '{tool_name: (.tool_name // "unknown")}' 2>/dev/null || echo '{}')
|
|
101
|
+
;;
|
|
102
|
+
WorktreeCreate)
|
|
103
|
+
TYPE="worktree_create"
|
|
104
|
+
META=$(echo "$INPUT" | jq -c '{worktree_path: (.worktree_path // "")}' 2>/dev/null || echo '{}')
|
|
105
|
+
;;
|
|
106
|
+
WorktreeRemove)
|
|
107
|
+
TYPE="worktree_remove"
|
|
108
|
+
META=$(echo "$INPUT" | jq -c '{worktree_path: (.worktree_path // "")}' 2>/dev/null || echo '{}')
|
|
109
|
+
;;
|
|
110
|
+
Elicitation|ElicitationResult)
|
|
111
|
+
TYPE=$(echo "$EVENT" | sed 's/Elicitation$/mcp_elicitation/;s/ElicitationResult/mcp_elicitation_result/')
|
|
112
|
+
META='{"tool":"claude_code"}'
|
|
113
|
+
;;
|
|
64
114
|
*)
|
|
65
115
|
TYPE="claude_code_event"
|
|
66
116
|
META="{\"event\":\"$EVENT\"}"
|
|
@@ -21,9 +21,23 @@ _HOOK_PY = _HOOKS_DIR / "claude_code.py"
|
|
|
21
21
|
HOOK_SCRIPT = _HOOK_PY if sys.platform == "win32" else _HOOK_SH
|
|
22
22
|
|
|
23
23
|
HOOK_EVENTS = [
|
|
24
|
+
# Core (already captured)
|
|
24
25
|
"UserPromptSubmit", "PreToolUse", "PostToolUse",
|
|
25
26
|
"SubagentStart", "SubagentStop",
|
|
26
27
|
"TaskCreated", "TaskCompleted", "SessionStart",
|
|
28
|
+
# New: interruptions, lifecycle, context
|
|
29
|
+
"PostToolUseFailure", # tool failure + is_interrupt flag
|
|
30
|
+
"SessionEnd", # session terminated
|
|
31
|
+
"Stop", # Claude finished responding (normal or interrupted)
|
|
32
|
+
"StopFailure", # turn ended due to API error
|
|
33
|
+
"CwdChanged", # working directory changed (ambiguous: human or Claude)
|
|
34
|
+
"PreCompact", "PostCompact", # context window compaction
|
|
35
|
+
"PermissionRequest", # permission dialog shown
|
|
36
|
+
"PermissionDenied", # auto mode denied a tool
|
|
37
|
+
"Elicitation", # MCP requested user input
|
|
38
|
+
"ElicitationResult", # user responded to MCP
|
|
39
|
+
"WorktreeCreate", # git worktree created (parallel agent)
|
|
40
|
+
"WorktreeRemove", # git worktree removed
|
|
27
41
|
]
|
|
28
42
|
|
|
29
43
|
# --- Codex CLI ---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "methodproof"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.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"]
|
|
@@ -862,7 +862,7 @@ wheels = [
|
|
|
862
862
|
|
|
863
863
|
[[package]]
|
|
864
864
|
name = "methodproof"
|
|
865
|
-
version = "0.
|
|
865
|
+
version = "0.6.0"
|
|
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
|