Pr 12#18
Conversation
The Rust hf-hub client uses a different TLS stack than Python and fails to reach huggingface.co in containers with custom CA certificates (UnknownIssuer). Add _auto_set_embed_offline() which runs at daemon startup: if the bge-small-en-v1.5 snapshot directory already exists in the HF cache, it sets IAI_MCP_EMBED_OFFLINE=1 automatically so the embedder skips the network download entirely. To seed the cache in a restricted environment, download the three model files (model.safetensors, tokenizer.json, config.json) via Python's SSL stack and place them under: ~/.cache/huggingface/hub/models--BAAI--bge-small-en-v1.5/snapshots/<rev>/ Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mf3VFyVtczcK2WxKKCyBS4
…tdfl63 Auto-detect HF model cache to bypass Rust TLS in restricted environments
Introduces src/iai_mcp/_ipc.py as the central abstraction for all daemon socket communication. On POSIX it delegates to the existing Unix-domain socket; on Windows it uses TCP loopback with the ephemeral port persisted in ~/.iai-mcp/.daemon.port. Replaces all nine raw asyncio.open_unix_connection / asyncio.start_unix_server / socket.AF_UNIX call-sites with the new open_ipc_connection / start_ipc_server / make_sync_ipc_socket helpers. The POSIX code-paths are structurally unchanged. This is step 1 of the Windows port. Remaining blockers (fcntl, resource module, POSIX signals, shell hooks, daemon installer) are tracked in the audit at the top of _ipc.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _filelock.py: on POSIX delegates to fcntl.flock; on Windows uses msvcrt.locking with EWOULDBLOCK normalisation so all non-blocking callers work unchanged. Also changes doctor check_c to open the lock file with O_RDWR (msvcrt.locking requires write access; harmless on POSIX). Updated 7 files: capture_queue, lifecycle_event_log, lifecycle, lock_protocol, hippo/_db, hippo/__init__ (dead import), doctor/_lifecycle_checks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resource module: lazy-import behind platform check (Windows has no RLIMIT_NOFILE). - Signals: build shutdown-signal list dynamically using hasattr; replace bare signal.SIGKILL with hasattr guard + sys.exit(1) fallback in _self_kill; use getattr for SIGTERM/SIGKILL in CLI stop and doctor orphan killer. - os.getuid(): guarded with hasattr fallback to 0 (4 sites in _daemon.py). - Log path: add _get_daemon_log_path() returning platform-appropriate path (%APPDATA%/iai-mcp/logs on Windows); add Windows branch to cmd_daemon_logs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- crypto.py: skip POSIX mode/uid checks on Windows (chmod is no-op); add _secure_key_file() using icacls for Windows ACL lockdown; guard os.fchmod with hasattr. - cli/_crypto.py: guard st.st_uid and os.geteuid() for status report. - memory_bank.py: guard os.fchmod call with hasattr. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds _is_windows() and SCHTASKS_TASK_NAME to cli/__init__.py and implements cmd_daemon_install/uninstall/start/stop branches for Windows in cli/_daemon.py using schtasks.exe and _render_schtasks_xml(). Also adds the Windows PowerShell turn-capture hook stub and the WINDOWS_PORT_HANDOFF.md guide for continuing the port in a new session (covers the remaining Steps 6-10 with file paths, code examples, and test commands). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates three PowerShell scripts (.ps1) to replace shell hooks on Windows: - iai-mcp-turn-capture.ps1: per-turn ambient capture (UserPromptSubmit) - iai-mcp-session-capture.ps1: batch-capture at session end (Stop) - iai-mcp-session-recall.ps1: session-start recall injection (SessionStart) Updates src/iai_mcp/cli/_capture.py: - _hook_ext() detects platform and returns '.ps1' on Windows, '.sh' on POSIX - _capture_hook_paths(), _turn_hook_paths(), _session_recall_hook_paths() now use _hook_ext() for dynamic extension - cmd_capture_hooks_install() uses 'powershell -ExecutionPolicy Bypass -File' for Windows hooks instead of 'bash' on POSIX - Markers (_CAPTURE_HOOK_MARKER, etc.) changed to base names (no extension) so substring matching works for both .sh and .ps1 in settings.json Updates pyproject.toml: - Adds "_deploy/hooks/*.ps1" to package.data so PowerShell hooks are bundled Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ps and verification checklist
The bench scripts measured peak RSS via resource.getrusage(RUSAGE_SELF).ru_maxrss, which is POSIX-only and crashes on import on Windows. Switch the Windows branch to psutil.Process().memory_info().peak_wset; POSIX paths are unchanged. - bench/memory_footprint.py: _rss_mb() guards on sys.platform == "win32" - bench/memorygraph_memory.py: rss_mb() same pattern - bench/consolidation_rss_peak.py: defer `import resource` into _ru_maxrss_bytes() - bench/embed_warm_cost.py: rewrite _PAYLOAD_RSS subprocess template to detect platform and pick peak_wset / ru_maxrss; emit rss_platform key; measure_rss decodes the new field for the unit label, returns rss_platform in result dict psutil is already a declared dependency (pyproject.toml), so no new package. Verified end-to-end on Windows: all three helpers and the rewritten payload produce sane values; AST-parse clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The fcntl→_filelock rewrite in commit 8154b9b swept timedelta and timezone into the new _filelock import line, but those belong to datetime. The module crashed with ImportError on any platform — masked until now because the Windows porting work hadn't tried to import the chain end-to-end. Move timedelta and timezone back to the `from datetime import` line. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 5 added Task Scheduler / schtasks backends to install/uninstall/start/ stop/logs, but the argparse help strings still mentioned only launchd and systemd. Update them to list all three platforms. The `Windows %APPDATA%\iai-mcp\logs` reference in the logs help is escaped as `%%APPDATA%%` so argparse's own %-formatter doesn't choke on it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a new "Verified on Windows in-situ" section recording what was actually exercised on a Windows machine in this session: AST parse over all 23 touched files, import smoke test of 10 runtime modules, CLI help, daemon install --dry-run producing a valid Task Scheduler XML, and capture-hooks status detecting the .ps1 templates. Also record the lifecycle_event_log fix and daemon help-text update commits alongside the existing Step-7 entry. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…m shims 5 test modules imported fcntl or resource at top level, aborting pytest collection before any test ran on Windows. Switch to the existing _filelock shim (LOCK_EX/SH/UN/NB + flock) and guard the resource import with a sys.platform check, skipping TestRaiseFdLimitClampsToHard on Windows (resource.RLIM_INFINITY is unavailable there). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs from @warplayer's Windows 11 test report: 1. _filelock.flock() was calling os.lseek(fd, 0, SEEK_SET) before msvcrt.locking(), which silently moved the file offset. fcntl.flock on POSIX never touches the offset; callers that need a specific byte already seek themselves. Removed the lseek to match POSIX behaviour. 2. _patch_claude_desktop_config() called _build_iai_mcp_server_entry() without a FileNotFoundError guard in two places (new-file creation and existing-file update). If the mcp-wrapper wasn't built yet and Claude Desktop was installed, the function would crash after already writing hooks and settings.json, leaving a partial install. Both call sites now catch FileNotFoundError and return an actionable skip message (consistent with the guard already on the Claude Code path). Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mf3VFyVtczcK2WxKKCyBS4
|
Huge thanks for taking this on — a Windows port is a big, cross-cutting piece of work and it means a lot that you're pushing on it. I'm following your commits and I want to land it once the port is complete (the PowerShell hooks wired up, the install/uninstall scripts, and ideally a run on a real Windows box to confirm it works end to end). No rush at all — take the time it needs, and I'll merge it as soon as it's ready. Genuinely appreciate the effort here. |
|
Closing in favor of #12, which is the canonical, complete Windows port (head = 🤖 Addressed by Claude Code |
Summary
Type of change
Affected areas
Testing
pytestpasses locallyruff check src/ tests/cleanBenchmarks
If this PR touches retrieval, capture, or consolidation, include before/after numbers from the relevant
bench.*harness.python -m bench.___Notes for reviewers