code-analysis-client-dev 1.6.341__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.
@@ -0,0 +1,251 @@
1
+ Metadata-Version: 2.4
2
+ Name: code-analysis-client-dev
3
+ Version: 1.6.341
4
+ Summary: Async JSON-RPC client for the code-analysis MCP server (mcp-proxy-adapter JsonRpcClient)
5
+ Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: mcp-proxy-adapter>=8.11.15
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest>=9.2.0; extra == "dev"
11
+ Requires-Dist: pytest-asyncio>=1.4.1.post1; extra == "dev"
12
+
13
+ # code-analysis-client
14
+
15
+ Async Python client for the **code-analysis** server. It wraps `mcp-proxy-adapter`’s `JsonRpcClient`, so you get the adapter’s built-in methods (queue, transfer, `help`, `health`, …) plus thin helpers to run any registered server command.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install code-analysis-client
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```python
26
+ import asyncio
27
+ from code_analysis_client import CodeAnalysisAsyncClient
28
+
29
+
30
+ async def main() -> None:
31
+ client = CodeAnalysisAsyncClient(
32
+ protocol="https",
33
+ host="127.0.0.1",
34
+ port=15001,
35
+ cert="/path/client.crt",
36
+ key="/path/client.key",
37
+ ca="/path/ca.crt",
38
+ timeout=120.0,
39
+ )
40
+ async with client:
41
+ h = await client.rpc.help()
42
+ r = await client.call("list_projects", {"include_deleted": False})
43
+ print(h, r)
44
+
45
+
46
+ asyncio.run(main())
47
+ ```
48
+
49
+ Build client settings from the same JSON shape as the pipeline adapter settings (`host`, `port`, `protocol`, optional `ssl` with `cert` / `key` / `ca` or `*_path` aliases), or from a full server `config.json` object.
50
+
51
+ ```python
52
+ from code_analysis_client import CodeAnalysisAsyncClient
53
+
54
+ client = CodeAnalysisAsyncClient.from_server_config(config_dict, timeout=60.0)
55
+ ```
56
+
57
+ ### Every call is the adapter's standard call
58
+
59
+ Every entry point — `call`, `call_validated`, `client.commands.<name>`, and the
60
+ `file_sessions` / `universal_files` / `worktrees` / `check_chains` facades built on
61
+ `call_validated` — goes through one core that uses `mcp-proxy-adapter`'s
62
+ `JsonRpcClient.execute_command_unified`: the command runs in a WebSocket command
63
+ session and the call returns when the terminal event arrives (no polling while the
64
+ socket is up). If `/ws` is unreachable the adapter falls back to RPC polling of
65
+ `queue_get_job_status` and logs one WARNING `WS_UNREACHABLE_RPC_POLLING_FALLBACK`;
66
+ the result is identical. The client has no polling loop of its own.
67
+
68
+ ```python
69
+ # Queued or not, this waits and returns the command's own result envelope.
70
+ out = await client.call("some_long_running_command", {...})
71
+ ```
72
+
73
+ Failures raise instead of returning an error envelope:
74
+
75
+ * `CommandFailedError` — the command ran but its inner result is
76
+ `{"success": false}`, or a queued job ended `failed` (immediate and queued
77
+ commands alike; a failing result is never returned as a dict). Carries
78
+ `.command`, `.job_id`, `.error` (the server's error object, e.g. `.error["code"]`)
79
+ and `.result` (the inner envelope, when there is one).
80
+ * `JobFailedError` — the job was stopped or cancelled, or the queue reported a
81
+ queue-level error. Carries `.job_id`, `.status` and `.error` (always a
82
+ structured error dict).
83
+ * `JobTimeoutError` — only when a positive `timeout` elapses; the call detaches and
84
+ the job keeps running server-side (`websocket.on_timeout` is `detach`).
85
+ * `ClientValidationError` — the server refused the parameters (JSON-RPC `-32602`),
86
+ answered at the top level or carried by the failed job of an in-process command
87
+ (same text `JSON-RPC error: <message> (code: -32602)`, `code` `VALIDATION_ERROR`);
88
+ `details` carries the server's error `data` (e.g. its `violation` payload). A
89
+ command the server refuses before starting it with its own error code (for
90
+ example `VALIDATION_ERROR` for an unknown `project_id`) raises it with that exact
91
+ `code` and the server's `data` as `details`.
92
+ * `ValueError` — a negative `timeout` or a non-positive `poll_interval`, before
93
+ anything is sent.
94
+
95
+ Optional keyword args on `call` / `call_validated` (and their `call_unified*`
96
+ counterparts):
97
+
98
+ * `timeout` — total wait in seconds. `None` (default) uses the client config
99
+ `websocket.job_wait_timeout`, shipped as `0` = no time limit, so a long queued
100
+ command is never cut off. A value above 0 raises `JobTimeoutError` and detaches.
101
+ * `poll_interval` — safety-net status-poll cadence in seconds. `None` (default)
102
+ uses `websocket.terminal_poll_interval` (2 s); a value overrides it per call.
103
+ * `status_hook` — sync or async callable invoked with each adapter WebSocket event
104
+ (at least the accepted event first and the terminal completed event last). An
105
+ exception raised by the hook propagates.
106
+
107
+ `call` / `call_validated` also take `auto_poll` (default `True`). Pass
108
+ `auto_poll=False` to detach as soon as the job id is known: the call returns a
109
+ `QueuedJob` handle immediately — for every command, including ones that finish at
110
+ once — and `timeout`, `poll_interval` and `status_hook` are ignored.
111
+
112
+ ```python
113
+ from code_analysis_client import QueuedJob
114
+
115
+ job = await client.call("some_long_running_command", {...}, auto_poll=False)
116
+ assert isinstance(job, QueuedJob)
117
+ # do other work here, then block on it whenever you're ready
118
+ result = await job.wait() # same result / errors as the default path
119
+ ```
120
+
121
+ `QueuedJob` exposes `.job_id`, `.envelope` (the adapter's acceptance envelope) and two
122
+ async methods: `.wait(timeout=None, poll_interval=None, status_hook=None)` — reads the
123
+ job once and, if it is still running, uses the adapter's WebSocket wait for the job id
124
+ (public API `wait_for_job_via_websocket`), then returns/raises exactly like the default
125
+ path; there is no RPC-polling fallback for an existing job id (a WebSocket failure
126
+ propagates) — and `.status()` — a single `queue_get_job_status` fetch.
127
+
128
+ #### Explicit adapter timeout configuration
129
+
130
+ The client hands `JsonRpcClient(ws_config=...)` the explicit defaults
131
+ `code_analysis_client.DEFAULT_CLIENT_ADAPTER_CONFIG` (a `ws_config=` keyword you pass is
132
+ merged over them key by key), so none of the adapter's own 60 s / `cancel` defaults is
133
+ effective:
134
+
135
+ | `websocket` key | value | meaning |
136
+ |---|---|---|
137
+ | `job_wait_timeout` | 0 | no total bound on a call's wait |
138
+ | `on_timeout` | `detach` | a wait timeout leaves the job running |
139
+ | `receive_timeout` / `receive_timeout_buffer` | 60 / 5 | one receive cap (never limits the total wait) |
140
+ | `heartbeat_interval` | 30 | WebSocket heartbeat |
141
+ | `terminal_poll_interval` | 2 | safety-net poll cadence |
142
+ | `close_timeout` | 5 | socket close bound |
143
+ | `subscribe_settle_delay` / `cancel_grace_seconds` | 0.5 / 0.5 | settle delay, cancel grace |
144
+
145
+ `job_push`: `on_caller_loss` `detach` (a job survives a lost client), `poll_interval` `0.1`.
146
+ The server ships and applies the same values.
147
+
148
+ `call_unified` / `call_unified_validated` are kept as **deprecated aliases** of
149
+ `call` / `call_validated` for backward compatibility and emit
150
+ `DeprecationWarning` on every call. `expect_queue` remains accepted-and-ignored
151
+ (documented no-op). `auto_poll` is the one canonical switch and is forwarded
152
+ straight through to `call` / `call_validated` — `auto_poll=False` on an alias
153
+ returns a `QueuedJob` the same way it does on the non-deprecated method.
154
+ Prefer `call` / `call_validated` directly.
155
+
156
+ ## Validation using the server schema
157
+
158
+ The authoritative input schema is whatever the running server returns from **`help`** with `cmdname` set to the command. The client calls that, optionally caches the result, performs the same shallow checks as the server’s `BaseMCPCommand` (types, `required`, `enum`, `additionalProperties`), then runs the command.
159
+
160
+ ```python
161
+ async with CodeAnalysisAsyncClient(host="127.0.0.1", port=15001) as client:
162
+ # Explicit
163
+ out = await client.call_validated(
164
+ "list_projects",
165
+ {"include_deleted": False},
166
+ )
167
+ # Dynamic wrapper: same as call_validated("list_projects", {...})
168
+ out = await client.commands.list_projects(include_deleted=False)
169
+ # After server reload
170
+ client.clear_command_schema_cache()
171
+ ```
172
+
173
+ Pass `refresh_schema=True` on a single call to bypass the in-memory schema cache.
174
+
175
+ ## High-level facades (aligned with live server registry)
176
+
177
+ The client does **not** wrap CST commands (`cst_load_file`, …) or legacy file I/O
178
+ (`universal_file_read`, `read_project_text_file`, …). Those commands are removed
179
+ from the server registry. Use the facades below or generic `call` / `commands.*`.
180
+
181
+ | Facade | Property | Server commands |
182
+ |--------|----------|-----------------|
183
+ | Client DB sessions + transfer | `client.file_sessions` | `session_*`, `subordinate_session_*`, `project_file_transfer_*`, `project_file_advisory_lock_batch` |
184
+ | Universal file preview | `client.universal_files` | `universal_file_preview` (read-only) |
185
+ | Any registered command | `client.call` / `client.commands.<name>` | schema from live `help()` |
186
+
187
+ `update_indexes` has no typed wrapper (it goes through the generic call); its optional
188
+ boolean `force` (default `false`) re-analyses every indexed file even when path+mtime are
189
+ unchanged, rewriting the tree sidecar and refreshing DB node ids:
190
+
191
+ ```python
192
+ await client.commands.update_indexes(project_id=pid, force=True)
193
+ ```
194
+
195
+ Canonical command lists: `code_analysis_client.server_api` — exported as
196
+ `FILE_SESSION_COMMANDS`, `FILE_SESSION_FACADE_METHODS`, `CLIENT_FACADE_COMMANDS`,
197
+ `REMOVED_COMMANDS`.
198
+
199
+ **Scope boundary:** this client manipulates files only as whole units — transfer,
200
+ locks, sessions, and structured read-only preview — and analyzes them. Content
201
+ editing is not served by this project's code-analysis server; use the ai-editor
202
+ client for that.
203
+
204
+ Sync checks (in-process registry):
205
+
206
+ ```bash
207
+ pytest tests/test_client_server_api_sync.py tests/test_code_analysis_client.py -k session
208
+ ```
209
+
210
+ Package version is in the root ``pyproject.toml``; before a client wheel build run
211
+ ``python scripts/sync_code_analysis_client_version.py`` (``./build.sh <VERSION>`` does it as its bump step).
212
+
213
+ ## Examples (this repository)
214
+
215
+ Runnable scripts live under `client/examples/`. **Long-form “man page” style
216
+ documentation** is embedded in the **module docstrings** of those Python files
217
+ (see `client/examples/README.md` for how to read them).
218
+
219
+ | Script | Purpose |
220
+ |--------|---------|
221
+ | `run_all_examples.py` | Full API tour + runs all live sibling scripts |
222
+ | `ex_minimal_validated.py` | Smallest validated RPC example |
223
+ | `ex_universal_files.py` | `UniversalFileClient.preview` (read-only structured preview) |
224
+ | `ex_session_view_subordinates.py` | `session_view` and subordinate CRUD |
225
+ | `ex_file_sessions.py` | Sessions, locks, transfer roundtrip |
226
+ | `ex_config_only.py` | Parse `config.json` without TCP |
227
+
228
+ ```bash
229
+ casmgr --config config.json start
230
+ python client/examples/run_all_examples.py
231
+ ```
232
+
233
+ ## Development
234
+
235
+ From the repository root:
236
+
237
+ ```bash
238
+ pip install -e ./client
239
+ pytest tests/test_code_analysis_client.py
240
+ ```
241
+
242
+ ### Releasing to PyPI (version = root ``code-analysis`` project)
243
+
244
+ The client wheel version is read from ``client/code_analysis_client/version.txt``.
245
+ That file must match ``[project].version`` in the **repository root**
246
+ ``pyproject.toml``. The client is published only by the repository release script,
247
+ together with the server image (same version, already-published versions fail):
248
+
249
+ ```bash
250
+ ./build.sh [dev=yes|no] <VERSION> # dev=yes (default) is the build pass that publishes the client
251
+ ```
@@ -0,0 +1,24 @@
1
+ code_analysis_client/__init__.py,sha256=zn0S9fMWGjUvJIE267ny1-IffRgs7LOHgGu_YCrIqIw,4066
2
+ code_analysis_client/check_chain.py,sha256=AUgY7r1hDNLELn6vtP42uLTVPgWDR4GcmxqK57hn6DU,13643
3
+ code_analysis_client/client.py,sha256=NWIp5ZyyFCz0ljckiSi0AoCSFY5n2wn5Yv5cPXo96xE,28079
4
+ code_analysis_client/commands_proxy.py,sha256=x5gC68QqH_AjTXbnosGCf_5OQXxQv8XSiEWi7lya74c,2226
5
+ code_analysis_client/config.py,sha256=I3jUYu8PDPKxazoJGUC13r7GfEBmzuCRuzPJz4jwe_E,7704
6
+ code_analysis_client/exceptions.py,sha256=emG6eVQAfXDsfUVRVCjCvwCFd7yoVLYZ0nkMUwXlrNw,6760
7
+ code_analysis_client/file_session.py,sha256=3EWzRIsB2rc33WjG-Psz6KeShVV-t97fPz4fglQA0A0,30623
8
+ code_analysis_client/project.py,sha256=oXgMO5pfuOQw5RLGTgNRARXI81b1-AemQezhe6yHRj8,2096
9
+ code_analysis_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ code_analysis_client/queue_wait.py,sha256=XLg_5qE82Y4u548iUCfNH3_tvJ7mhuO8JjrjgAd0jDw,15795
11
+ code_analysis_client/responses.py,sha256=8xGS0ag7lwpJ6eW2IrsmaI4los8sgHlHBO5zxAGNUbI,4659
12
+ code_analysis_client/schema_disk_cache.py,sha256=sJsy8pEgMTdcDVUr1fLmR8JFDKdcXE03GddQLLAInXw,8090
13
+ code_analysis_client/server_api.py,sha256=ggYaThbjgblvpiaUvjl6MYOEVkjuC5Wr-b5PQJjaRA0,10558
14
+ code_analysis_client/server_schema.py,sha256=6auc4WQFiA_2-XQDNVX-8edPtkYCZP4IOzVlePMfYj0,1862
15
+ code_analysis_client/symbol_tree.py,sha256=nZpYSttb7IAGzfkfkZUG0GJ3Zt6QU0rNkfaGAMpJ4n4,279
16
+ code_analysis_client/universal_file.py,sha256=Ha2XVZg3dcBSpnfRBiGpK1z_kVlHEPPjaRkpaNSxspU,1638
17
+ code_analysis_client/validation.py,sha256=ETUaWW2aGEFaWoXHPWj1n4hwQMfpek3ua3PmrEQJ-yc,3918
18
+ code_analysis_client/vectorizer_state.py,sha256=uZYbBnnsWt1-_gzGMVNaC2LGAEEzt69DpYI-o4GvDHI,5480
19
+ code_analysis_client/version.txt,sha256=ULvJV3vkYhEKP-17egc0wT8VFUjJ8t9zRHmcg8V36Dw,8
20
+ code_analysis_client/worktree.py,sha256=tpZDMxt2osS2WbLM9jE_QK-OSFoZbUtlNe7a2xFDtB4,15801
21
+ code_analysis_client_dev-1.6.341.dist-info/METADATA,sha256=F9qFqP-qPYSwPz-rMrUj-qB-r5Pr9q_cYgvWDPFUNns,11336
22
+ code_analysis_client_dev-1.6.341.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
23
+ code_analysis_client_dev-1.6.341.dist-info/top_level.txt,sha256=NXzwlTVdqyKm43HrnRqBc-DlAeSVkHifJihWZ9JZ2xs,21
24
+ code_analysis_client_dev-1.6.341.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ code_analysis_client