ai-editor-client 1.0.65__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.
- ai_editor_client-1.0.65/PKG-INFO +212 -0
- ai_editor_client-1.0.65/README.md +200 -0
- ai_editor_client-1.0.65/ai_editor_client/__init__.py +180 -0
- ai_editor_client-1.0.65/ai_editor_client/client.py +267 -0
- ai_editor_client-1.0.65/ai_editor_client/commands_proxy.py +60 -0
- ai_editor_client-1.0.65/ai_editor_client/config.py +126 -0
- ai_editor_client-1.0.65/ai_editor_client/editor_file.py +202 -0
- ai_editor_client-1.0.65/ai_editor_client/exceptions.py +20 -0
- ai_editor_client-1.0.65/ai_editor_client/file_session.py +682 -0
- ai_editor_client-1.0.65/ai_editor_client/local_edit_workspace.py +102 -0
- ai_editor_client-1.0.65/ai_editor_client/py.typed +0 -0
- ai_editor_client-1.0.65/ai_editor_client/responses.py +45 -0
- ai_editor_client-1.0.65/ai_editor_client/server_api.py +220 -0
- ai_editor_client-1.0.65/ai_editor_client/server_schema.py +57 -0
- ai_editor_client-1.0.65/ai_editor_client/universal_file.py +140 -0
- ai_editor_client-1.0.65/ai_editor_client/validation.py +98 -0
- ai_editor_client-1.0.65/ai_editor_client/version.txt +1 -0
- ai_editor_client-1.0.65/ai_editor_client.egg-info/PKG-INFO +212 -0
- ai_editor_client-1.0.65/ai_editor_client.egg-info/SOURCES.txt +22 -0
- ai_editor_client-1.0.65/ai_editor_client.egg-info/dependency_links.txt +1 -0
- ai_editor_client-1.0.65/ai_editor_client.egg-info/requires.txt +5 -0
- ai_editor_client-1.0.65/ai_editor_client.egg-info/top_level.txt +1 -0
- ai_editor_client-1.0.65/pyproject.toml +32 -0
- ai_editor_client-1.0.65/setup.cfg +4 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ai-editor-client
|
|
3
|
+
Version: 1.0.65
|
|
4
|
+
Summary: Async JSON-RPC client for the ai-editor 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.10.15
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-asyncio>=0.25.0; 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. On the thin AI Editor Server, file editing uses `universal_file_*` commands only; queue polling helpers remain for generic RPC but are not part of the file workflow surface.
|
|
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
|
+
Queued/long commands: use `client.call_unified(..., expect_queue=True, auto_poll=True)` or the underlying `client.rpc.execute_command_unified(...)`.
|
|
58
|
+
|
|
59
|
+
## Validation using the server schema
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
async with CodeAnalysisAsyncClient(host="127.0.0.1", port=15001) as client:
|
|
65
|
+
# Explicit
|
|
66
|
+
out = await client.call_validated(
|
|
67
|
+
"list_projects",
|
|
68
|
+
{"include_deleted": False},
|
|
69
|
+
)
|
|
70
|
+
# Dynamic wrapper: same as call_validated("list_projects", {...})
|
|
71
|
+
out = await client.commands.list_projects(include_deleted=False)
|
|
72
|
+
# After server reload
|
|
73
|
+
client.clear_command_schema_cache()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Use `call_unified_validated` when you need queue polling. Pass `refresh_schema=True` on a single call to bypass the in-memory schema cache.
|
|
77
|
+
|
|
78
|
+
## File Workflow (C-009)
|
|
79
|
+
|
|
80
|
+
The thin AI Editor Server exposes file editing only through `UniversalFileClient` (`client.universal_files`). One file at a time follows **open → edit → write → close**. Optional read-only inspection during an open file uses `preview` (`universal_file_preview`). Every stage requires the same CA Session `session_id`, `project_id`, and (where applicable) `file_path`.
|
|
81
|
+
|
|
82
|
+
### Workflow stages
|
|
83
|
+
|
|
84
|
+
1. **Open** (`universal_file_open`) — agent supplies CA `session_id`; server locks file on upstream CA, creates local Editor Session Directory and File Subtree, stores Origin Snapshot, creates Edit Subdirectory. Use `create=True` with `initial_content` for new files.
|
|
85
|
+
2. **Edit** (`universal_file_edit`) — mutations apply only to the Edit Subdirectory; Origin Snapshot unchanged.
|
|
86
|
+
3. **Write** (`universal_file_write`) — compares Origin Snapshot to edited content; `write_mode="preview"` returns local diff without upstream upload; commit mode uploads to CA on change and refreshes Origin Snapshot on success.
|
|
87
|
+
4. **Close** (`universal_file_close`) — unlocks file on CA, removes local File Subtree; removes Editor Session Directory when last file in session closes.
|
|
88
|
+
|
|
89
|
+
### Minimal async example
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import asyncio
|
|
93
|
+
import uuid
|
|
94
|
+
|
|
95
|
+
from ai_editor_client import CodeAnalysisAsyncClient
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def main() -> None:
|
|
99
|
+
# Created upstream via CA session_create (outside this client package).
|
|
100
|
+
ca_session_id = str(uuid.uuid4())
|
|
101
|
+
|
|
102
|
+
async with CodeAnalysisAsyncClient.from_server_config_path("config.json") as client:
|
|
103
|
+
uf = client.universal_files
|
|
104
|
+
project_id = "..."
|
|
105
|
+
file_path = "src/example.py"
|
|
106
|
+
|
|
107
|
+
await uf.open(
|
|
108
|
+
session_id=ca_session_id,
|
|
109
|
+
project_id=project_id,
|
|
110
|
+
file_path=file_path,
|
|
111
|
+
)
|
|
112
|
+
await uf.preview(
|
|
113
|
+
session_id=ca_session_id,
|
|
114
|
+
project_id=project_id,
|
|
115
|
+
file_path=file_path,
|
|
116
|
+
)
|
|
117
|
+
await uf.edit(
|
|
118
|
+
session_id=ca_session_id,
|
|
119
|
+
project_id=project_id,
|
|
120
|
+
file_path=file_path,
|
|
121
|
+
operations=[{"action": "replace", "start_line": 1, "end_line": 1, "code": "# edited\n"}],
|
|
122
|
+
)
|
|
123
|
+
await uf.write(
|
|
124
|
+
session_id=ca_session_id,
|
|
125
|
+
project_id=project_id,
|
|
126
|
+
file_path=file_path,
|
|
127
|
+
write_mode="preview",
|
|
128
|
+
)
|
|
129
|
+
await uf.close(
|
|
130
|
+
session_id=ca_session_id,
|
|
131
|
+
project_id=project_id,
|
|
132
|
+
file_path=file_path,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
asyncio.run(main())
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Full runnable script: `client/examples/ex_universal_files.py`.
|
|
140
|
+
|
|
141
|
+
### CA Session ID contract (C-003)
|
|
142
|
+
|
|
143
|
+
- The **agent** creates the CA Session on Code Analysis Server (`session_create`) **before** calling any editor `universal_file_*` command. That RPC is **not** part of the thin editor MCP surface and is **not** wrapped by `UniversalFileClient`.
|
|
144
|
+
- The agent passes the **same** `session_id` string to every `universal_file_open`, `universal_file_edit`, `universal_file_write`, `universal_file_close`, and `universal_file_preview` call for the workflow.
|
|
145
|
+
- Do **not** treat the `open` response as the source of `session_id` for later calls. If the server echoes `session_id`, it must match the agent-supplied value; the agent already owns the identifier.
|
|
146
|
+
- The agent decides when to `write` (commit or preview) and `close`; the client library does not manage CA session lifecycle.
|
|
147
|
+
|
|
148
|
+
## High-level facades (thin AI Editor Server / C-016)
|
|
149
|
+
|
|
150
|
+
The supported public MCP commands for file workflow are `UNIVERSAL_FILE_COMMANDS` plus `health` only (`CLIENT_FACADE_COMMANDS` in `ai_editor_client.server_api`). CST commands (`cst_*`, `list_cst_blocks`, `query_cst`) and legacy direct file I/O are in `REMOVED_COMMANDS` — not on the thin server, not documented as supported. `client.universal_files` is the **only** supported high-level facade for file editing.
|
|
151
|
+
|
|
152
|
+
| Facade | Property | Server commands | Status |
|
|
153
|
+
|--------|----------|-----------------|--------|
|
|
154
|
+
| Universal file workflow | `client.universal_files` | `universal_file_open`, `universal_file_edit`, `universal_file_write`, `universal_file_close`, `universal_file_preview` | **Supported** (C-009 / C-016) |
|
|
155
|
+
| Infrastructure | `client.call("health", {})` or adapter `health` | `health` | **Supported** |
|
|
156
|
+
| Generic RPC | `client.call` / `client.commands.<name>` | any command returned by live `help()` on connected server | Escape hatch; thin server exposes only rows above for files |
|
|
157
|
+
| Legacy sessions + transfer | `client.file_sessions` | `session_*`, `subordinate_session_*`, `project_file_transfer_*`, `project_file_advisory_lock_batch` | **Deprecated** — `DEPRECATED_CLIENT_FACADE_COMMANDS`; not registered on thin editor server |
|
|
158
|
+
|
|
159
|
+
Canonical command lists: `ai_editor_client.server_api` exports `UNIVERSAL_FILE_COMMANDS`, `INFRASTRUCTURE_COMMANDS`, `CLIENT_FACADE_COMMANDS`, `DEPRECATED_CLIENT_FACADE_COMMANDS`, and `REMOVED_COMMANDS`.
|
|
160
|
+
|
|
161
|
+
Sync checks (in-process registry):
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pytest tests/test_client_server_api_sync.py -v
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
These tests assert `CLIENT_FACADE_COMMANDS` matches the live server registry (C-016) and `REMOVED_COMMANDS` are absent from the server (C-022).
|
|
168
|
+
|
|
169
|
+
Package version is in ``client/ai_editor_client/version.txt`` (synced with the
|
|
170
|
+
root ``code-analysis`` project via ``scripts/sync_code_analysis_client_version.py``).
|
|
171
|
+
|
|
172
|
+
## Examples (this repository)
|
|
173
|
+
|
|
174
|
+
Runnable scripts live under `client/examples/`. **Long-form "man page" style
|
|
175
|
+
documentation** is embedded in the **module docstrings** of those Python files
|
|
176
|
+
(see `client/examples/README.md` for how to read them).
|
|
177
|
+
|
|
178
|
+
| Script | Purpose |
|
|
179
|
+
|--------|---------|
|
|
180
|
+
| `ex_universal_files.py` | **Primary:** C-009 File Workflow demo — all `UniversalFileClient` methods with agent-supplied CA `session_id` (C-003) |
|
|
181
|
+
| `ex_minimal_validated.py` | Smallest validated RPC example (`health` / generic call) |
|
|
182
|
+
| `ex_config_only.py` | Parse `config.json` without TCP |
|
|
183
|
+
| `run_all_examples.py` | Runs sibling live scripts (includes universal files demo) |
|
|
184
|
+
| `ex_session_view_subordinates.py` | **Deprecated** — legacy CA session API; not thin editor MCP |
|
|
185
|
+
| `ex_file_sessions.py` | **Deprecated** — legacy sessions/transfer; not thin editor MCP |
|
|
186
|
+
|
|
187
|
+
For file editing on thin AI Editor Server, start with `ex_universal_files.py`; do not use `ex_file_sessions.py` or `ex_session_view_subordinates.py` as file workflow templates.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
aiedmgr --config config.json start
|
|
191
|
+
python client/examples/ex_universal_files.py
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Development
|
|
195
|
+
|
|
196
|
+
From the repository root:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
pip install -e ./client
|
|
200
|
+
pytest tests/test_code_analysis_client.py
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Releasing to PyPI (version = root ``code-analysis`` project)
|
|
204
|
+
|
|
205
|
+
The client wheel version is read from ``client/code_analysis_client/version.txt``.
|
|
206
|
+
That file must match ``[project].version`` in the **repository root**
|
|
207
|
+
``pyproject.toml``. Sync before build:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
python scripts/sync_code_analysis_client_version.py
|
|
211
|
+
cd client && python -m build && twine check dist/* && twine upload dist/*
|
|
212
|
+
```
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# code-analysis-client
|
|
2
|
+
|
|
3
|
+
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. On the thin AI Editor Server, file editing uses `universal_file_*` commands only; queue polling helpers remain for generic RPC but are not part of the file workflow surface.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install code-analysis-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import asyncio
|
|
15
|
+
from code_analysis_client import CodeAnalysisAsyncClient
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def main() -> None:
|
|
19
|
+
client = CodeAnalysisAsyncClient(
|
|
20
|
+
protocol="https",
|
|
21
|
+
host="127.0.0.1",
|
|
22
|
+
port=15001,
|
|
23
|
+
cert="/path/client.crt",
|
|
24
|
+
key="/path/client.key",
|
|
25
|
+
ca="/path/ca.crt",
|
|
26
|
+
timeout=120.0,
|
|
27
|
+
)
|
|
28
|
+
async with client:
|
|
29
|
+
h = await client.rpc.help()
|
|
30
|
+
r = await client.call("list_projects", {"include_deleted": False})
|
|
31
|
+
print(h, r)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
asyncio.run(main())
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
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.
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from code_analysis_client import CodeAnalysisAsyncClient
|
|
41
|
+
|
|
42
|
+
client = CodeAnalysisAsyncClient.from_server_config(config_dict, timeout=60.0)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Queued/long commands: use `client.call_unified(..., expect_queue=True, auto_poll=True)` or the underlying `client.rpc.execute_command_unified(...)`.
|
|
46
|
+
|
|
47
|
+
## Validation using the server schema
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
async with CodeAnalysisAsyncClient(host="127.0.0.1", port=15001) as client:
|
|
53
|
+
# Explicit
|
|
54
|
+
out = await client.call_validated(
|
|
55
|
+
"list_projects",
|
|
56
|
+
{"include_deleted": False},
|
|
57
|
+
)
|
|
58
|
+
# Dynamic wrapper: same as call_validated("list_projects", {...})
|
|
59
|
+
out = await client.commands.list_projects(include_deleted=False)
|
|
60
|
+
# After server reload
|
|
61
|
+
client.clear_command_schema_cache()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use `call_unified_validated` when you need queue polling. Pass `refresh_schema=True` on a single call to bypass the in-memory schema cache.
|
|
65
|
+
|
|
66
|
+
## File Workflow (C-009)
|
|
67
|
+
|
|
68
|
+
The thin AI Editor Server exposes file editing only through `UniversalFileClient` (`client.universal_files`). One file at a time follows **open → edit → write → close**. Optional read-only inspection during an open file uses `preview` (`universal_file_preview`). Every stage requires the same CA Session `session_id`, `project_id`, and (where applicable) `file_path`.
|
|
69
|
+
|
|
70
|
+
### Workflow stages
|
|
71
|
+
|
|
72
|
+
1. **Open** (`universal_file_open`) — agent supplies CA `session_id`; server locks file on upstream CA, creates local Editor Session Directory and File Subtree, stores Origin Snapshot, creates Edit Subdirectory. Use `create=True` with `initial_content` for new files.
|
|
73
|
+
2. **Edit** (`universal_file_edit`) — mutations apply only to the Edit Subdirectory; Origin Snapshot unchanged.
|
|
74
|
+
3. **Write** (`universal_file_write`) — compares Origin Snapshot to edited content; `write_mode="preview"` returns local diff without upstream upload; commit mode uploads to CA on change and refreshes Origin Snapshot on success.
|
|
75
|
+
4. **Close** (`universal_file_close`) — unlocks file on CA, removes local File Subtree; removes Editor Session Directory when last file in session closes.
|
|
76
|
+
|
|
77
|
+
### Minimal async example
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import asyncio
|
|
81
|
+
import uuid
|
|
82
|
+
|
|
83
|
+
from ai_editor_client import CodeAnalysisAsyncClient
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def main() -> None:
|
|
87
|
+
# Created upstream via CA session_create (outside this client package).
|
|
88
|
+
ca_session_id = str(uuid.uuid4())
|
|
89
|
+
|
|
90
|
+
async with CodeAnalysisAsyncClient.from_server_config_path("config.json") as client:
|
|
91
|
+
uf = client.universal_files
|
|
92
|
+
project_id = "..."
|
|
93
|
+
file_path = "src/example.py"
|
|
94
|
+
|
|
95
|
+
await uf.open(
|
|
96
|
+
session_id=ca_session_id,
|
|
97
|
+
project_id=project_id,
|
|
98
|
+
file_path=file_path,
|
|
99
|
+
)
|
|
100
|
+
await uf.preview(
|
|
101
|
+
session_id=ca_session_id,
|
|
102
|
+
project_id=project_id,
|
|
103
|
+
file_path=file_path,
|
|
104
|
+
)
|
|
105
|
+
await uf.edit(
|
|
106
|
+
session_id=ca_session_id,
|
|
107
|
+
project_id=project_id,
|
|
108
|
+
file_path=file_path,
|
|
109
|
+
operations=[{"action": "replace", "start_line": 1, "end_line": 1, "code": "# edited\n"}],
|
|
110
|
+
)
|
|
111
|
+
await uf.write(
|
|
112
|
+
session_id=ca_session_id,
|
|
113
|
+
project_id=project_id,
|
|
114
|
+
file_path=file_path,
|
|
115
|
+
write_mode="preview",
|
|
116
|
+
)
|
|
117
|
+
await uf.close(
|
|
118
|
+
session_id=ca_session_id,
|
|
119
|
+
project_id=project_id,
|
|
120
|
+
file_path=file_path,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
asyncio.run(main())
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Full runnable script: `client/examples/ex_universal_files.py`.
|
|
128
|
+
|
|
129
|
+
### CA Session ID contract (C-003)
|
|
130
|
+
|
|
131
|
+
- The **agent** creates the CA Session on Code Analysis Server (`session_create`) **before** calling any editor `universal_file_*` command. That RPC is **not** part of the thin editor MCP surface and is **not** wrapped by `UniversalFileClient`.
|
|
132
|
+
- The agent passes the **same** `session_id` string to every `universal_file_open`, `universal_file_edit`, `universal_file_write`, `universal_file_close`, and `universal_file_preview` call for the workflow.
|
|
133
|
+
- Do **not** treat the `open` response as the source of `session_id` for later calls. If the server echoes `session_id`, it must match the agent-supplied value; the agent already owns the identifier.
|
|
134
|
+
- The agent decides when to `write` (commit or preview) and `close`; the client library does not manage CA session lifecycle.
|
|
135
|
+
|
|
136
|
+
## High-level facades (thin AI Editor Server / C-016)
|
|
137
|
+
|
|
138
|
+
The supported public MCP commands for file workflow are `UNIVERSAL_FILE_COMMANDS` plus `health` only (`CLIENT_FACADE_COMMANDS` in `ai_editor_client.server_api`). CST commands (`cst_*`, `list_cst_blocks`, `query_cst`) and legacy direct file I/O are in `REMOVED_COMMANDS` — not on the thin server, not documented as supported. `client.universal_files` is the **only** supported high-level facade for file editing.
|
|
139
|
+
|
|
140
|
+
| Facade | Property | Server commands | Status |
|
|
141
|
+
|--------|----------|-----------------|--------|
|
|
142
|
+
| Universal file workflow | `client.universal_files` | `universal_file_open`, `universal_file_edit`, `universal_file_write`, `universal_file_close`, `universal_file_preview` | **Supported** (C-009 / C-016) |
|
|
143
|
+
| Infrastructure | `client.call("health", {})` or adapter `health` | `health` | **Supported** |
|
|
144
|
+
| Generic RPC | `client.call` / `client.commands.<name>` | any command returned by live `help()` on connected server | Escape hatch; thin server exposes only rows above for files |
|
|
145
|
+
| Legacy sessions + transfer | `client.file_sessions` | `session_*`, `subordinate_session_*`, `project_file_transfer_*`, `project_file_advisory_lock_batch` | **Deprecated** — `DEPRECATED_CLIENT_FACADE_COMMANDS`; not registered on thin editor server |
|
|
146
|
+
|
|
147
|
+
Canonical command lists: `ai_editor_client.server_api` exports `UNIVERSAL_FILE_COMMANDS`, `INFRASTRUCTURE_COMMANDS`, `CLIENT_FACADE_COMMANDS`, `DEPRECATED_CLIENT_FACADE_COMMANDS`, and `REMOVED_COMMANDS`.
|
|
148
|
+
|
|
149
|
+
Sync checks (in-process registry):
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pytest tests/test_client_server_api_sync.py -v
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
These tests assert `CLIENT_FACADE_COMMANDS` matches the live server registry (C-016) and `REMOVED_COMMANDS` are absent from the server (C-022).
|
|
156
|
+
|
|
157
|
+
Package version is in ``client/ai_editor_client/version.txt`` (synced with the
|
|
158
|
+
root ``code-analysis`` project via ``scripts/sync_code_analysis_client_version.py``).
|
|
159
|
+
|
|
160
|
+
## Examples (this repository)
|
|
161
|
+
|
|
162
|
+
Runnable scripts live under `client/examples/`. **Long-form "man page" style
|
|
163
|
+
documentation** is embedded in the **module docstrings** of those Python files
|
|
164
|
+
(see `client/examples/README.md` for how to read them).
|
|
165
|
+
|
|
166
|
+
| Script | Purpose |
|
|
167
|
+
|--------|---------|
|
|
168
|
+
| `ex_universal_files.py` | **Primary:** C-009 File Workflow demo — all `UniversalFileClient` methods with agent-supplied CA `session_id` (C-003) |
|
|
169
|
+
| `ex_minimal_validated.py` | Smallest validated RPC example (`health` / generic call) |
|
|
170
|
+
| `ex_config_only.py` | Parse `config.json` without TCP |
|
|
171
|
+
| `run_all_examples.py` | Runs sibling live scripts (includes universal files demo) |
|
|
172
|
+
| `ex_session_view_subordinates.py` | **Deprecated** — legacy CA session API; not thin editor MCP |
|
|
173
|
+
| `ex_file_sessions.py` | **Deprecated** — legacy sessions/transfer; not thin editor MCP |
|
|
174
|
+
|
|
175
|
+
For file editing on thin AI Editor Server, start with `ex_universal_files.py`; do not use `ex_file_sessions.py` or `ex_session_view_subordinates.py` as file workflow templates.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
aiedmgr --config config.json start
|
|
179
|
+
python client/examples/ex_universal_files.py
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
From the repository root:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pip install -e ./client
|
|
188
|
+
pytest tests/test_code_analysis_client.py
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Releasing to PyPI (version = root ``code-analysis`` project)
|
|
192
|
+
|
|
193
|
+
The client wheel version is read from ``client/code_analysis_client/version.txt``.
|
|
194
|
+
That file must match ``[project].version`` in the **repository root**
|
|
195
|
+
``pyproject.toml``. Sync before build:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
python scripts/sync_code_analysis_client_version.py
|
|
199
|
+
cd client && python -m build && twine check dist/* && twine upload dist/*
|
|
200
|
+
```
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Public async client for ai-editor-server (JSON-RPC via mcp-proxy-adapter).
|
|
3
|
+
|
|
4
|
+
``UniversalFileClient`` (via :attr:`~ai_editor_client.client.CodeAnalysisAsyncClient.universal_files`)
|
|
5
|
+
is the single supported file-workflow facade for the thin MCP Workflow Surface (C-016).
|
|
6
|
+
``FileSessionClient``, ``EditorFileClient``, ``EditorFileHandle``, and ``LocalEditWorkspace``
|
|
7
|
+
are deprecated parallel facades (C-020) and emit :class:`DeprecationWarning` on instantiation.
|
|
8
|
+
|
|
9
|
+
Author: Vasiliy Zdanovskiy
|
|
10
|
+
email: vasilyvz@gmail.com
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import warnings
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
from ai_editor_client.client import CodeAnalysisAsyncClient
|
|
20
|
+
from ai_editor_client.commands_proxy import ValidatedCommandsProxy
|
|
21
|
+
from ai_editor_client.config import (
|
|
22
|
+
adapter_settings_from_server_config,
|
|
23
|
+
adapter_settings_to_jsonrpc_kwargs,
|
|
24
|
+
load_server_config,
|
|
25
|
+
)
|
|
26
|
+
from ai_editor_client.editor_file import (
|
|
27
|
+
EditorFileClient as _EditorFileClient,
|
|
28
|
+
EditorFileHandle as _EditorFileHandle,
|
|
29
|
+
)
|
|
30
|
+
from ai_editor_client.exceptions import ClientValidationError
|
|
31
|
+
from ai_editor_client.file_session import (
|
|
32
|
+
FileSessionClient as _FileSessionClient,
|
|
33
|
+
SessionNotFoundError,
|
|
34
|
+
)
|
|
35
|
+
from ai_editor_client.local_edit_workspace import (
|
|
36
|
+
EditorFileWorkflowError,
|
|
37
|
+
LocalEditWorkspace as _LocalEditWorkspace,
|
|
38
|
+
)
|
|
39
|
+
from ai_editor_client.server_api import (
|
|
40
|
+
CLIENT_FACADE_COMMANDS,
|
|
41
|
+
CST_REMOVED_COMMANDS,
|
|
42
|
+
FILE_SESSION_COMMANDS,
|
|
43
|
+
FILE_SESSION_FACADE_METHODS,
|
|
44
|
+
LEGACY_REMOVED_COMMANDS,
|
|
45
|
+
REMOVED_COMMANDS,
|
|
46
|
+
TRANSFER_FACADE_METHODS,
|
|
47
|
+
UNIVERSAL_FILE_COMMANDS,
|
|
48
|
+
)
|
|
49
|
+
from ai_editor_client.universal_file import UniversalFileClient
|
|
50
|
+
from ai_editor_client.server_schema import (
|
|
51
|
+
fetch_command_schema_from_server,
|
|
52
|
+
parse_schema_from_help_payload,
|
|
53
|
+
)
|
|
54
|
+
from ai_editor_client.validation import (
|
|
55
|
+
prepare_params_for_schema,
|
|
56
|
+
validate_params_against_schema,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
_DEPRECATED_EXPORTS: frozenset[str] = frozenset(
|
|
60
|
+
{
|
|
61
|
+
"FileSessionClient",
|
|
62
|
+
"EditorFileClient",
|
|
63
|
+
"EditorFileHandle",
|
|
64
|
+
"LocalEditWorkspace",
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _emit_deprecated_export_warning(class_name: str) -> None:
|
|
70
|
+
warnings.warn(
|
|
71
|
+
(
|
|
72
|
+
f"{class_name} export is deprecated; use UniversalFileClient via "
|
|
73
|
+
"CodeAnalysisAsyncClient.universal_files (C-016). Removal under C-020."
|
|
74
|
+
),
|
|
75
|
+
DeprecationWarning,
|
|
76
|
+
stacklevel=3,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _DeprecatedFileSessionClient(_FileSessionClient):
|
|
81
|
+
def __init__(self, client: CodeAnalysisAsyncClient) -> None:
|
|
82
|
+
_emit_deprecated_export_warning("FileSessionClient")
|
|
83
|
+
super().__init__(client)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class _DeprecatedEditorFileClient(_EditorFileClient):
|
|
87
|
+
def __init__(self, file_sessions: FileSessionClient) -> None:
|
|
88
|
+
_emit_deprecated_export_warning("EditorFileClient")
|
|
89
|
+
super().__init__(file_sessions)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class _DeprecatedEditorFileHandle(_EditorFileHandle):
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
ca_session_id: str,
|
|
96
|
+
project_id: str,
|
|
97
|
+
file_id: str,
|
|
98
|
+
file_path: str,
|
|
99
|
+
baseline_path: Path,
|
|
100
|
+
workspace: Optional[_LocalEditWorkspace] = None,
|
|
101
|
+
is_closed: bool = False,
|
|
102
|
+
) -> None:
|
|
103
|
+
_emit_deprecated_export_warning("EditorFileHandle")
|
|
104
|
+
super().__init__(
|
|
105
|
+
ca_session_id=ca_session_id,
|
|
106
|
+
project_id=project_id,
|
|
107
|
+
file_id=file_id,
|
|
108
|
+
file_path=file_path,
|
|
109
|
+
baseline_path=baseline_path,
|
|
110
|
+
workspace=workspace,
|
|
111
|
+
is_closed=is_closed,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class _DeprecatedLocalEditWorkspace(_LocalEditWorkspace):
|
|
116
|
+
def __init__(
|
|
117
|
+
self,
|
|
118
|
+
workspace_id: str,
|
|
119
|
+
baseline_path: Path,
|
|
120
|
+
session_dir: Path,
|
|
121
|
+
working_path: Path,
|
|
122
|
+
is_open: bool = True,
|
|
123
|
+
) -> None:
|
|
124
|
+
_emit_deprecated_export_warning("LocalEditWorkspace")
|
|
125
|
+
super().__init__(
|
|
126
|
+
workspace_id=workspace_id,
|
|
127
|
+
baseline_path=baseline_path,
|
|
128
|
+
session_dir=session_dir,
|
|
129
|
+
working_path=working_path,
|
|
130
|
+
is_open=is_open,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
FileSessionClient = _DeprecatedFileSessionClient
|
|
135
|
+
EditorFileClient = _DeprecatedEditorFileClient
|
|
136
|
+
EditorFileHandle = _DeprecatedEditorFileHandle
|
|
137
|
+
LocalEditWorkspace = _DeprecatedLocalEditWorkspace
|
|
138
|
+
|
|
139
|
+
__all__ = [
|
|
140
|
+
"CLIENT_FACADE_COMMANDS",
|
|
141
|
+
"CST_REMOVED_COMMANDS",
|
|
142
|
+
"ClientValidationError",
|
|
143
|
+
"CodeAnalysisAsyncClient",
|
|
144
|
+
"EditorFileClient",
|
|
145
|
+
"EditorFileHandle",
|
|
146
|
+
"EditorFileWorkflowError",
|
|
147
|
+
"FILE_SESSION_COMMANDS",
|
|
148
|
+
"FILE_SESSION_FACADE_METHODS",
|
|
149
|
+
"FileSessionClient",
|
|
150
|
+
"LEGACY_REMOVED_COMMANDS",
|
|
151
|
+
"LocalEditWorkspace",
|
|
152
|
+
"REMOVED_COMMANDS",
|
|
153
|
+
"SessionNotFoundError",
|
|
154
|
+
"TRANSFER_FACADE_METHODS",
|
|
155
|
+
"UNIVERSAL_FILE_COMMANDS",
|
|
156
|
+
"UniversalFileClient",
|
|
157
|
+
"ValidatedCommandsProxy",
|
|
158
|
+
"adapter_settings_from_server_config",
|
|
159
|
+
"adapter_settings_to_jsonrpc_kwargs",
|
|
160
|
+
"fetch_command_schema_from_server",
|
|
161
|
+
"load_server_config",
|
|
162
|
+
"parse_schema_from_help_payload",
|
|
163
|
+
"prepare_params_for_schema",
|
|
164
|
+
"validate_params_against_schema",
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _read_package_version() -> str:
|
|
169
|
+
vf = Path(__file__).resolve().parent / "version.txt"
|
|
170
|
+
if vf.is_file():
|
|
171
|
+
return vf.read_text(encoding="utf-8").strip()
|
|
172
|
+
try:
|
|
173
|
+
import importlib.metadata as _imd
|
|
174
|
+
|
|
175
|
+
return _imd.version("ai-editor-client")
|
|
176
|
+
except Exception:
|
|
177
|
+
return "0.0.0"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
__version__ = _read_package_version()
|