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.
- code_analysis_client/__init__.py +142 -0
- code_analysis_client/check_chain.py +374 -0
- code_analysis_client/client.py +728 -0
- code_analysis_client/commands_proxy.py +63 -0
- code_analysis_client/config.py +198 -0
- code_analysis_client/exceptions.py +188 -0
- code_analysis_client/file_session.py +823 -0
- code_analysis_client/project.py +59 -0
- code_analysis_client/py.typed +0 -0
- code_analysis_client/queue_wait.py +341 -0
- code_analysis_client/responses.py +126 -0
- code_analysis_client/schema_disk_cache.py +207 -0
- code_analysis_client/server_api.py +291 -0
- code_analysis_client/server_schema.py +57 -0
- code_analysis_client/symbol_tree.py +13 -0
- code_analysis_client/universal_file.py +50 -0
- code_analysis_client/validation.py +98 -0
- code_analysis_client/vectorizer_state.py +159 -0
- code_analysis_client/version.txt +1 -0
- code_analysis_client/worktree.py +533 -0
- code_analysis_client_dev-1.6.341.dist-info/METADATA +251 -0
- code_analysis_client_dev-1.6.341.dist-info/RECORD +24 -0
- code_analysis_client_dev-1.6.341.dist-info/WHEEL +5 -0
- code_analysis_client_dev-1.6.341.dist-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
1
|
+
code_analysis_client
|