codecortex 0.2.2__tar.gz → 0.4.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.
Potentially problematic release.
This version of codecortex might be problematic. Click here for more details.
- {codecortex-0.2.2/src/codecortex.egg-info → codecortex-0.4.0}/PKG-INFO +44 -9
- {codecortex-0.2.2 → codecortex-0.4.0}/README.md +43 -8
- {codecortex-0.2.2 → codecortex-0.4.0/src/codecortex.egg-info}/PKG-INFO +44 -9
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codecortex.egg-info/SOURCES.txt +6 -0
- codecortex-0.4.0/src/codeintel/__init__.py +1 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/__main__.py +6 -3
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/cache.py +1 -1
- codecortex-0.4.0/src/codeintel/config.py +97 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/gateway.py +9 -6
- codecortex-0.4.0/src/codeintel/http_server.py +294 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/indexer.py +12 -1
- codecortex-0.4.0/src/codeintel/logconfig.py +54 -0
- codecortex-0.4.0/src/codeintel/metrics.py +87 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/onboarding.py +1 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/provider.py +19 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/providers/graph.py +17 -7
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/providers/lsp.py +3 -2
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/providers/semantic.py +23 -10
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/reindexer.py +26 -3
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/server.py +13 -5
- codecortex-0.4.0/tests/test_config.py +63 -0
- codecortex-0.4.0/tests/test_enterprise.py +145 -0
- codecortex-0.4.0/tests/test_hardening.py +182 -0
- codecortex-0.4.0/tests/test_http_auth.py +121 -0
- codecortex-0.2.2/src/codeintel/__init__.py +0 -1
- codecortex-0.2.2/src/codeintel/config.py +0 -42
- codecortex-0.2.2/src/codeintel/http_server.py +0 -97
- {codecortex-0.2.2 → codecortex-0.4.0}/LICENSE +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/pyproject.toml +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/setup.cfg +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codecortex.egg-info/dependency_links.txt +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codecortex.egg-info/entry_points.txt +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codecortex.egg-info/requires.txt +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codecortex.egg-info/top_level.txt +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/doctor.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/injector.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/installer.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/mapper.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/policy.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/providers/__init__.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/providers/none.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/reset.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/searcher.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/semantic_db.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/src/codeintel/term.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_cache.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_doctor.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_e2e.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_gateway.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_graph_provider.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_graph_real.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_graph_stdin.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_http_server.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_integration.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_lsp_provider.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_lsp_real.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_mapper.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_never_raise.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_onboarding.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_reindexer.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_reset.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_semantic_provider.py +0 -0
- {codecortex-0.2.2 → codecortex-0.4.0}/tests/test_term.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
|
|
5
5
|
Author: Shammai Hamilton
|
|
6
6
|
License-Expression: MIT
|
|
@@ -183,7 +183,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
183
183
|
| `codeintel setup [project_root] [--index] [--warm] [--install-uv]` | Check backends + optionally index this repo; ends with a health report |
|
|
184
184
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
185
185
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
186
|
-
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote]` | Start the HTTP transport (loopback-only unless `--allow-remote`) |
|
|
186
|
+
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
|
|
187
187
|
| `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
|
|
188
188
|
| `codeintel status [project_root]` | Show engine availability and index age |
|
|
189
189
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
@@ -197,17 +197,31 @@ Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--n
|
|
|
197
197
|
Create `.codeintel.toml` at your project root to override defaults:
|
|
198
198
|
|
|
199
199
|
```toml
|
|
200
|
-
backend
|
|
201
|
-
semantic
|
|
202
|
-
reindex
|
|
203
|
-
cosine_floor
|
|
204
|
-
max_chunks
|
|
205
|
-
|
|
200
|
+
backend = "auto" # auto | graph | lsp | semantic
|
|
201
|
+
semantic = "on" # on | off
|
|
202
|
+
reindex = "on-demand" # on-demand | never
|
|
203
|
+
cosine_floor = 0.25 # minimum similarity score for semantic hits (0–1)
|
|
204
|
+
max_chunks = 500 # max chunks to embed per file
|
|
205
|
+
max_total_chunks = 100000 # safety ceiling on chunks embedded in one index pass
|
|
206
|
+
model = "BAAI/bge-small-en-v1.5" # fastembed embedding model
|
|
206
207
|
```
|
|
207
208
|
|
|
209
|
+
Config is **validated on load** — an out-of-range number, a misspelled enum, or a wrong type falls back to that key's default (with a logged warning) instead of breaking every query.
|
|
210
|
+
|
|
211
|
+
**Environment variables:**
|
|
212
|
+
|
|
213
|
+
| Variable | Effect |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `CODEINTEL_HTTP_TOKEN` | Bearer token required by `serve-http` (equivalent to `--token`) |
|
|
216
|
+
| `CODEINTEL_LOG_LEVEL` | `DEBUG`\|`INFO`\|`WARNING`(default)\|`ERROR` for the server logger |
|
|
217
|
+
| `CODEINTEL_LOG_FORMAT=json` | Structured (JSON-per-line) logs for ELK / Splunk / Datadog |
|
|
218
|
+
| `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
|
|
219
|
+
| `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
|
|
220
|
+
| `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
|
|
221
|
+
|
|
208
222
|
## Privacy & dependencies
|
|
209
223
|
|
|
210
|
-
**codeintel is local-first** — one local process, no cloud service, no API keys, no telemetry, and no per-query network. Its own code makes zero outbound HTTP calls, and the HTTP transport binds to `127.0.0.1` only.
|
|
224
|
+
**codeintel is local-first** — one local process, no cloud service, no API keys, no telemetry, and no per-query network. Its own code makes zero outbound HTTP calls, and the HTTP transport binds to `127.0.0.1` only by default — binding a non-loopback host requires `--allow-remote`, and `--token` (or `CODEINTEL_HTTP_TOKEN`) then gates every request behind a bearer token. The server bounds concurrent connections, but for exposure to a hostile network you should still front it with a reverse proxy (TLS, rate-limiting) — the built-in `http.server` is not hardened for the open internet.
|
|
211
225
|
|
|
212
226
|
**Bundled (installed with the package, run locally):** `mcp` (the tool interface) · `sqlite-vec` (the semantic index, a local DB file) · `fastembed` (the local embedding model).
|
|
213
227
|
|
|
@@ -240,6 +254,8 @@ Over MCP the agent calls `code.query` directly. Over HTTP, start the server and
|
|
|
240
254
|
codeintel serve-http & # listens on 127.0.0.1:8766 by default
|
|
241
255
|
```
|
|
242
256
|
|
|
257
|
+
For a shared or remote deployment, start it with `--allow-remote --token "$CODEINTEL_HTTP_TOKEN"` and send `Authorization: Bearer <token>` on each request — a missing or wrong token gets a clean `401`. Requests are handled concurrently, so one slow query never blocks another.
|
|
258
|
+
|
|
243
259
|
```python
|
|
244
260
|
import urllib.request, json
|
|
245
261
|
|
|
@@ -260,6 +276,25 @@ if result["result"] is not None:
|
|
|
260
276
|
|
|
261
277
|
The response is always JSON-safe. Check `result["result"] is not None` before use. Never catch an exception from the gateway — it never raises.
|
|
262
278
|
|
|
279
|
+
## Operations & deployment
|
|
280
|
+
|
|
281
|
+
Running codeintel as a shared service? It ships with what ops teams expect:
|
|
282
|
+
|
|
283
|
+
| Endpoint | Auth | Purpose |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| `GET /healthz` | none | Liveness — always `200` (for load balancers / `livenessProbe`) |
|
|
286
|
+
| `GET /readyz` | none | Readiness — `200` once the gateway is up (`readinessProbe`) |
|
|
287
|
+
| `GET /metrics` | token | Prometheus exposition — request counts, latency, in-flight, build info |
|
|
288
|
+
|
|
289
|
+
Plus **bearer-token auth**, **structured JSON logs** (`CODEINTEL_LOG_FORMAT=json`) with optional per-request access logs, **graceful `SIGTERM`** shutdown, a bounded connection pool, and a non-root **Dockerfile** with a healthcheck.
|
|
290
|
+
|
|
291
|
+
**Full guide → [docs/deploy.md](docs/deploy.md)**: systemd, Docker / Compose, Kubernetes (liveness + readiness probes, token from a Secret), reverse-proxy TLS, a Prometheus scrape config, and a security checklist.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
|
|
295
|
+
-e CODEINTEL_HTTP_TOKEN="$(openssl rand -hex 32)" codeintel
|
|
296
|
+
```
|
|
297
|
+
|
|
263
298
|
## Development
|
|
264
299
|
|
|
265
300
|
```bash
|
|
@@ -150,7 +150,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
150
150
|
| `codeintel setup [project_root] [--index] [--warm] [--install-uv]` | Check backends + optionally index this repo; ends with a health report |
|
|
151
151
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
152
152
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
153
|
-
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote]` | Start the HTTP transport (loopback-only unless `--allow-remote`) |
|
|
153
|
+
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
|
|
154
154
|
| `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
|
|
155
155
|
| `codeintel status [project_root]` | Show engine availability and index age |
|
|
156
156
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
@@ -164,17 +164,31 @@ Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--n
|
|
|
164
164
|
Create `.codeintel.toml` at your project root to override defaults:
|
|
165
165
|
|
|
166
166
|
```toml
|
|
167
|
-
backend
|
|
168
|
-
semantic
|
|
169
|
-
reindex
|
|
170
|
-
cosine_floor
|
|
171
|
-
max_chunks
|
|
172
|
-
|
|
167
|
+
backend = "auto" # auto | graph | lsp | semantic
|
|
168
|
+
semantic = "on" # on | off
|
|
169
|
+
reindex = "on-demand" # on-demand | never
|
|
170
|
+
cosine_floor = 0.25 # minimum similarity score for semantic hits (0–1)
|
|
171
|
+
max_chunks = 500 # max chunks to embed per file
|
|
172
|
+
max_total_chunks = 100000 # safety ceiling on chunks embedded in one index pass
|
|
173
|
+
model = "BAAI/bge-small-en-v1.5" # fastembed embedding model
|
|
173
174
|
```
|
|
174
175
|
|
|
176
|
+
Config is **validated on load** — an out-of-range number, a misspelled enum, or a wrong type falls back to that key's default (with a logged warning) instead of breaking every query.
|
|
177
|
+
|
|
178
|
+
**Environment variables:**
|
|
179
|
+
|
|
180
|
+
| Variable | Effect |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `CODEINTEL_HTTP_TOKEN` | Bearer token required by `serve-http` (equivalent to `--token`) |
|
|
183
|
+
| `CODEINTEL_LOG_LEVEL` | `DEBUG`\|`INFO`\|`WARNING`(default)\|`ERROR` for the server logger |
|
|
184
|
+
| `CODEINTEL_LOG_FORMAT=json` | Structured (JSON-per-line) logs for ELK / Splunk / Datadog |
|
|
185
|
+
| `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
|
|
186
|
+
| `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
|
|
187
|
+
| `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
|
|
188
|
+
|
|
175
189
|
## Privacy & dependencies
|
|
176
190
|
|
|
177
|
-
**codeintel is local-first** — one local process, no cloud service, no API keys, no telemetry, and no per-query network. Its own code makes zero outbound HTTP calls, and the HTTP transport binds to `127.0.0.1` only.
|
|
191
|
+
**codeintel is local-first** — one local process, no cloud service, no API keys, no telemetry, and no per-query network. Its own code makes zero outbound HTTP calls, and the HTTP transport binds to `127.0.0.1` only by default — binding a non-loopback host requires `--allow-remote`, and `--token` (or `CODEINTEL_HTTP_TOKEN`) then gates every request behind a bearer token. The server bounds concurrent connections, but for exposure to a hostile network you should still front it with a reverse proxy (TLS, rate-limiting) — the built-in `http.server` is not hardened for the open internet.
|
|
178
192
|
|
|
179
193
|
**Bundled (installed with the package, run locally):** `mcp` (the tool interface) · `sqlite-vec` (the semantic index, a local DB file) · `fastembed` (the local embedding model).
|
|
180
194
|
|
|
@@ -207,6 +221,8 @@ Over MCP the agent calls `code.query` directly. Over HTTP, start the server and
|
|
|
207
221
|
codeintel serve-http & # listens on 127.0.0.1:8766 by default
|
|
208
222
|
```
|
|
209
223
|
|
|
224
|
+
For a shared or remote deployment, start it with `--allow-remote --token "$CODEINTEL_HTTP_TOKEN"` and send `Authorization: Bearer <token>` on each request — a missing or wrong token gets a clean `401`. Requests are handled concurrently, so one slow query never blocks another.
|
|
225
|
+
|
|
210
226
|
```python
|
|
211
227
|
import urllib.request, json
|
|
212
228
|
|
|
@@ -227,6 +243,25 @@ if result["result"] is not None:
|
|
|
227
243
|
|
|
228
244
|
The response is always JSON-safe. Check `result["result"] is not None` before use. Never catch an exception from the gateway — it never raises.
|
|
229
245
|
|
|
246
|
+
## Operations & deployment
|
|
247
|
+
|
|
248
|
+
Running codeintel as a shared service? It ships with what ops teams expect:
|
|
249
|
+
|
|
250
|
+
| Endpoint | Auth | Purpose |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| `GET /healthz` | none | Liveness — always `200` (for load balancers / `livenessProbe`) |
|
|
253
|
+
| `GET /readyz` | none | Readiness — `200` once the gateway is up (`readinessProbe`) |
|
|
254
|
+
| `GET /metrics` | token | Prometheus exposition — request counts, latency, in-flight, build info |
|
|
255
|
+
|
|
256
|
+
Plus **bearer-token auth**, **structured JSON logs** (`CODEINTEL_LOG_FORMAT=json`) with optional per-request access logs, **graceful `SIGTERM`** shutdown, a bounded connection pool, and a non-root **Dockerfile** with a healthcheck.
|
|
257
|
+
|
|
258
|
+
**Full guide → [docs/deploy.md](docs/deploy.md)**: systemd, Docker / Compose, Kubernetes (liveness + readiness probes, token from a Secret), reverse-proxy TLS, a Prometheus scrape config, and a security checklist.
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
|
|
262
|
+
-e CODEINTEL_HTTP_TOKEN="$(openssl rand -hex 32)" codeintel
|
|
263
|
+
```
|
|
264
|
+
|
|
230
265
|
## Development
|
|
231
266
|
|
|
232
267
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Local-first, MCP-native code-intelligence server — graph, LSP, and semantic search behind one safe code.query tool for coding agents.
|
|
5
5
|
Author: Shammai Hamilton
|
|
6
6
|
License-Expression: MIT
|
|
@@ -183,7 +183,7 @@ Full system docs live in [`docs/`](docs/) — start with the index:
|
|
|
183
183
|
| `codeintel setup [project_root] [--index] [--warm] [--install-uv]` | Check backends + optionally index this repo; ends with a health report |
|
|
184
184
|
| `codeintel index [project_root]` | Index a project for semantic search |
|
|
185
185
|
| `codeintel serve` | Start the MCP server (stdio transport) |
|
|
186
|
-
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote]` | Start the HTTP transport (loopback-only unless `--allow-remote`) |
|
|
186
|
+
| `codeintel serve-http [--host HOST] [--port 8766] [--allow-remote] [--token TOKEN]` | Start the HTTP transport (loopback-only unless `--allow-remote`; `--token` requires a bearer token on every request) |
|
|
187
187
|
| `codeintel query --op OP --target TARGET [--engine auto]` | Run a single query and print the result |
|
|
188
188
|
| `codeintel status [project_root]` | Show engine availability and index age |
|
|
189
189
|
| `codeintel doctor [project_root] [--deep] [--json]` | Diagnose per-engine health + repo index status, with a fix for each gap |
|
|
@@ -197,17 +197,31 @@ Human-facing commands (`doctor`, `status`, `query`, `setup`, `reset`) honor `--n
|
|
|
197
197
|
Create `.codeintel.toml` at your project root to override defaults:
|
|
198
198
|
|
|
199
199
|
```toml
|
|
200
|
-
backend
|
|
201
|
-
semantic
|
|
202
|
-
reindex
|
|
203
|
-
cosine_floor
|
|
204
|
-
max_chunks
|
|
205
|
-
|
|
200
|
+
backend = "auto" # auto | graph | lsp | semantic
|
|
201
|
+
semantic = "on" # on | off
|
|
202
|
+
reindex = "on-demand" # on-demand | never
|
|
203
|
+
cosine_floor = 0.25 # minimum similarity score for semantic hits (0–1)
|
|
204
|
+
max_chunks = 500 # max chunks to embed per file
|
|
205
|
+
max_total_chunks = 100000 # safety ceiling on chunks embedded in one index pass
|
|
206
|
+
model = "BAAI/bge-small-en-v1.5" # fastembed embedding model
|
|
206
207
|
```
|
|
207
208
|
|
|
209
|
+
Config is **validated on load** — an out-of-range number, a misspelled enum, or a wrong type falls back to that key's default (with a logged warning) instead of breaking every query.
|
|
210
|
+
|
|
211
|
+
**Environment variables:**
|
|
212
|
+
|
|
213
|
+
| Variable | Effect |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `CODEINTEL_HTTP_TOKEN` | Bearer token required by `serve-http` (equivalent to `--token`) |
|
|
216
|
+
| `CODEINTEL_LOG_LEVEL` | `DEBUG`\|`INFO`\|`WARNING`(default)\|`ERROR` for the server logger |
|
|
217
|
+
| `CODEINTEL_LOG_FORMAT=json` | Structured (JSON-per-line) logs for ELK / Splunk / Datadog |
|
|
218
|
+
| `CODEINTEL_HTTP_ACCESS_LOG=1` | One log line per HTTP request (method, path, status, latency) |
|
|
219
|
+
| `CODEINTEL_DEBUG=1` | Log the full traceback of any error the never-throw contract swallows (silent by default) — the switch for diagnosing an unexpected `null` |
|
|
220
|
+
| `CODEINTEL_REINDEX=off` | Disable the background reindexer; queries then index inline to stay fresh |
|
|
221
|
+
|
|
208
222
|
## Privacy & dependencies
|
|
209
223
|
|
|
210
|
-
**codeintel is local-first** — one local process, no cloud service, no API keys, no telemetry, and no per-query network. Its own code makes zero outbound HTTP calls, and the HTTP transport binds to `127.0.0.1` only.
|
|
224
|
+
**codeintel is local-first** — one local process, no cloud service, no API keys, no telemetry, and no per-query network. Its own code makes zero outbound HTTP calls, and the HTTP transport binds to `127.0.0.1` only by default — binding a non-loopback host requires `--allow-remote`, and `--token` (or `CODEINTEL_HTTP_TOKEN`) then gates every request behind a bearer token. The server bounds concurrent connections, but for exposure to a hostile network you should still front it with a reverse proxy (TLS, rate-limiting) — the built-in `http.server` is not hardened for the open internet.
|
|
211
225
|
|
|
212
226
|
**Bundled (installed with the package, run locally):** `mcp` (the tool interface) · `sqlite-vec` (the semantic index, a local DB file) · `fastembed` (the local embedding model).
|
|
213
227
|
|
|
@@ -240,6 +254,8 @@ Over MCP the agent calls `code.query` directly. Over HTTP, start the server and
|
|
|
240
254
|
codeintel serve-http & # listens on 127.0.0.1:8766 by default
|
|
241
255
|
```
|
|
242
256
|
|
|
257
|
+
For a shared or remote deployment, start it with `--allow-remote --token "$CODEINTEL_HTTP_TOKEN"` and send `Authorization: Bearer <token>` on each request — a missing or wrong token gets a clean `401`. Requests are handled concurrently, so one slow query never blocks another.
|
|
258
|
+
|
|
243
259
|
```python
|
|
244
260
|
import urllib.request, json
|
|
245
261
|
|
|
@@ -260,6 +276,25 @@ if result["result"] is not None:
|
|
|
260
276
|
|
|
261
277
|
The response is always JSON-safe. Check `result["result"] is not None` before use. Never catch an exception from the gateway — it never raises.
|
|
262
278
|
|
|
279
|
+
## Operations & deployment
|
|
280
|
+
|
|
281
|
+
Running codeintel as a shared service? It ships with what ops teams expect:
|
|
282
|
+
|
|
283
|
+
| Endpoint | Auth | Purpose |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| `GET /healthz` | none | Liveness — always `200` (for load balancers / `livenessProbe`) |
|
|
286
|
+
| `GET /readyz` | none | Readiness — `200` once the gateway is up (`readinessProbe`) |
|
|
287
|
+
| `GET /metrics` | token | Prometheus exposition — request counts, latency, in-flight, build info |
|
|
288
|
+
|
|
289
|
+
Plus **bearer-token auth**, **structured JSON logs** (`CODEINTEL_LOG_FORMAT=json`) with optional per-request access logs, **graceful `SIGTERM`** shutdown, a bounded connection pool, and a non-root **Dockerfile** with a healthcheck.
|
|
290
|
+
|
|
291
|
+
**Full guide → [docs/deploy.md](docs/deploy.md)**: systemd, Docker / Compose, Kubernetes (liveness + readiness probes, token from a Secret), reverse-proxy TLS, a Prometheus scrape config, and a security checklist.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
docker build -t codeintel . && docker run -p 127.0.0.1:8766:8766 \
|
|
295
|
+
-e CODEINTEL_HTTP_TOKEN="$(openssl rand -hex 32)" codeintel
|
|
296
|
+
```
|
|
297
|
+
|
|
263
298
|
## Development
|
|
264
299
|
|
|
265
300
|
```bash
|
|
@@ -17,7 +17,9 @@ src/codeintel/http_server.py
|
|
|
17
17
|
src/codeintel/indexer.py
|
|
18
18
|
src/codeintel/injector.py
|
|
19
19
|
src/codeintel/installer.py
|
|
20
|
+
src/codeintel/logconfig.py
|
|
20
21
|
src/codeintel/mapper.py
|
|
22
|
+
src/codeintel/metrics.py
|
|
21
23
|
src/codeintel/onboarding.py
|
|
22
24
|
src/codeintel/policy.py
|
|
23
25
|
src/codeintel/provider.py
|
|
@@ -33,12 +35,16 @@ src/codeintel/providers/lsp.py
|
|
|
33
35
|
src/codeintel/providers/none.py
|
|
34
36
|
src/codeintel/providers/semantic.py
|
|
35
37
|
tests/test_cache.py
|
|
38
|
+
tests/test_config.py
|
|
36
39
|
tests/test_doctor.py
|
|
37
40
|
tests/test_e2e.py
|
|
41
|
+
tests/test_enterprise.py
|
|
38
42
|
tests/test_gateway.py
|
|
39
43
|
tests/test_graph_provider.py
|
|
40
44
|
tests/test_graph_real.py
|
|
41
45
|
tests/test_graph_stdin.py
|
|
46
|
+
tests/test_hardening.py
|
|
47
|
+
tests/test_http_auth.py
|
|
42
48
|
tests/test_http_server.py
|
|
43
49
|
tests/test_integration.py
|
|
44
50
|
tests/test_lsp_provider.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0"
|
|
@@ -50,7 +50,8 @@ def main() -> None:
|
|
|
50
50
|
http_parser = subparsers.add_parser("serve-http", help="Start the HTTP transport server")
|
|
51
51
|
http_parser.add_argument("--port", type=int, default=8766, help="Port to listen on (default: 8766)")
|
|
52
52
|
http_parser.add_argument("--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)")
|
|
53
|
-
http_parser.add_argument("--allow-remote", action="store_true", help="Permit binding a non-loopback host (
|
|
53
|
+
http_parser.add_argument("--allow-remote", action="store_true", help="Permit binding a non-loopback host (use with --token, or the endpoint is UNAUTHENTICATED)")
|
|
54
|
+
http_parser.add_argument("--token", default=None, help="Require this bearer token on every request (or set CODEINTEL_HTTP_TOKEN). Strongly recommended with --allow-remote.")
|
|
54
55
|
|
|
55
56
|
# install subcommand
|
|
56
57
|
install_parser = subparsers.add_parser("install", help="Register codeintel with AI agents")
|
|
@@ -120,6 +121,7 @@ def main() -> None:
|
|
|
120
121
|
window=int(cfg.get("window", 20)),
|
|
121
122
|
stride=int(cfg.get("stride", 10)),
|
|
122
123
|
max_chunks=int(cfg.get("max_chunks", 500)),
|
|
124
|
+
max_total_chunks=int(cfg.get("max_total_chunks", 100000)),
|
|
123
125
|
).index(project_root)
|
|
124
126
|
if count > 0:
|
|
125
127
|
print(f"Indexed {count} chunks")
|
|
@@ -225,7 +227,7 @@ def main() -> None:
|
|
|
225
227
|
from codeintel import server
|
|
226
228
|
|
|
227
229
|
project_root = args.project_root or os.getcwd()
|
|
228
|
-
status = server.code_status_handler({})
|
|
230
|
+
status = server.code_status_handler({"project_root": project_root})
|
|
229
231
|
|
|
230
232
|
print("Engine status:")
|
|
231
233
|
for engine in ["graph", "lsp", "semantic"]:
|
|
@@ -318,7 +320,8 @@ def main() -> None:
|
|
|
318
320
|
elif args.command == "serve-http":
|
|
319
321
|
try:
|
|
320
322
|
from codeintel.http_server import run
|
|
321
|
-
|
|
323
|
+
token = args.token or os.environ.get("CODEINTEL_HTTP_TOKEN")
|
|
324
|
+
run(host=args.host, port=args.port, allow_remote=args.allow_remote, token=token)
|
|
322
325
|
except KeyboardInterrupt:
|
|
323
326
|
pass
|
|
324
327
|
except Exception as exc:
|
|
@@ -13,7 +13,7 @@ def _compute_hash(target: str, project_root: str) -> str:
|
|
|
13
13
|
try:
|
|
14
14
|
root = os.path.realpath(project_root) if project_root else ""
|
|
15
15
|
path = os.path.realpath(target)
|
|
16
|
-
if root and path.startswith(root + os.sep) or path == root:
|
|
16
|
+
if (root and path.startswith(root + os.sep)) or path == root:
|
|
17
17
|
if os.path.isfile(path):
|
|
18
18
|
with open(path, "rb") as fh:
|
|
19
19
|
return hashlib.sha256(fh.read()).hexdigest()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import pathlib
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
if sys.version_info >= (3, 11):
|
|
8
|
+
import tomllib
|
|
9
|
+
else: # pragma: no cover
|
|
10
|
+
try:
|
|
11
|
+
import tomllib # type: ignore[no-redef]
|
|
12
|
+
except ImportError:
|
|
13
|
+
import tomli as tomllib # type: ignore[no-redef]
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("codeintel")
|
|
16
|
+
|
|
17
|
+
_DEFAULTS: dict = {
|
|
18
|
+
"backend": "auto",
|
|
19
|
+
"semantic": "on",
|
|
20
|
+
"reindex": "on-demand",
|
|
21
|
+
"window": 20,
|
|
22
|
+
"stride": 10,
|
|
23
|
+
"max_chunks": 500, # per file
|
|
24
|
+
"max_total_chunks": 100000, # safety ceiling on chunks embedded in one index pass
|
|
25
|
+
"cosine_floor": 0.25,
|
|
26
|
+
"model": "BAAI/bge-small-en-v1.5",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Values restricted to a fixed set — anything else falls back to the default.
|
|
30
|
+
_ENUMS: dict = {
|
|
31
|
+
"backend": {"auto", "graph", "lsp", "semantic"},
|
|
32
|
+
"semantic": {"on", "off"},
|
|
33
|
+
"reindex": {"on-demand", "never"},
|
|
34
|
+
}
|
|
35
|
+
_POSITIVE_INTS = ("window", "stride", "max_chunks", "max_total_chunks")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _read_toml(path: pathlib.Path) -> dict:
|
|
39
|
+
try:
|
|
40
|
+
with path.open("rb") as fh:
|
|
41
|
+
return tomllib.load(fh)
|
|
42
|
+
except (FileNotFoundError, OSError, tomllib.TOMLDecodeError):
|
|
43
|
+
return {}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _coerce(cfg: dict) -> dict:
|
|
47
|
+
"""Coerce/clamp a merged config to safe values, warning on and dropping bad ones. Never raises:
|
|
48
|
+
a malformed ``.codeintel.toml`` (a string where a number belongs, an out-of-range floor, an
|
|
49
|
+
unknown enum) must degrade to the default for that key, not break every query that loads it."""
|
|
50
|
+
out = dict(_DEFAULTS)
|
|
51
|
+
for key, default in _DEFAULTS.items():
|
|
52
|
+
if key not in cfg:
|
|
53
|
+
continue
|
|
54
|
+
val = cfg[key]
|
|
55
|
+
try:
|
|
56
|
+
if key in _ENUMS:
|
|
57
|
+
s = str(val).strip().lower()
|
|
58
|
+
if s in _ENUMS[key]:
|
|
59
|
+
out[key] = s
|
|
60
|
+
else:
|
|
61
|
+
logger.warning("config: %s=%r invalid (expected %s) — using %r",
|
|
62
|
+
key, val, sorted(_ENUMS[key]), default)
|
|
63
|
+
elif key == "model":
|
|
64
|
+
out[key] = str(val).strip() or default
|
|
65
|
+
elif key == "cosine_floor":
|
|
66
|
+
f = float(val)
|
|
67
|
+
if f != f: # NaN (TOML allows `nan`) — reject so it can't silently disable the floor
|
|
68
|
+
raise ValueError("nan")
|
|
69
|
+
out[key] = min(1.0, max(0.0, f))
|
|
70
|
+
elif key in _POSITIVE_INTS:
|
|
71
|
+
n = int(val)
|
|
72
|
+
if n > 0:
|
|
73
|
+
out[key] = n
|
|
74
|
+
else:
|
|
75
|
+
logger.warning("config: %s=%r must be > 0 — using %r", key, val, default)
|
|
76
|
+
except (TypeError, ValueError, OverflowError):
|
|
77
|
+
# int(float('inf')) raises OverflowError (TOML allows `inf`); float("x") → ValueError;
|
|
78
|
+
# int([]) → TypeError. Any non-usable value logs and keeps the default, so the
|
|
79
|
+
# docstring's never-raise promise holds even for the CLI paths that don't wrap this.
|
|
80
|
+
logger.warning("config: %s=%r not usable — using default %r", key, val, default)
|
|
81
|
+
# Keep any extra keys the user set (forward-compat) without validating them.
|
|
82
|
+
for k, v in cfg.items():
|
|
83
|
+
if k not in _DEFAULTS:
|
|
84
|
+
out[k] = v
|
|
85
|
+
return out
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def load_config(project_root: str | None = None) -> dict:
|
|
89
|
+
"""Return the merged, validated config: defaults < global < project. Values that fail
|
|
90
|
+
validation fall back to their default (logged), so a bad config file never breaks a query."""
|
|
91
|
+
root = pathlib.Path(project_root) if project_root is not None else pathlib.Path.cwd()
|
|
92
|
+
|
|
93
|
+
global_cfg = _read_toml(pathlib.Path.home() / ".codeintel" / "config.toml")
|
|
94
|
+
project_cfg = _read_toml(root / ".codeintel.toml")
|
|
95
|
+
|
|
96
|
+
merged = {**_DEFAULTS, **global_cfg, **project_cfg}
|
|
97
|
+
return _coerce(merged)
|
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
from codeintel.cache import ContentHashCache
|
|
7
7
|
from codeintel.policy import TieringPolicy
|
|
8
|
-
from codeintel.provider import Result, safe_null_result
|
|
8
|
+
from codeintel.provider import Result, log_swallowed, safe_null_result
|
|
9
9
|
from codeintel.providers.none import NoneProvider
|
|
10
10
|
from codeintel.reindexer import Reindexer
|
|
11
11
|
|
|
@@ -128,7 +128,8 @@ class Gateway:
|
|
|
128
128
|
if r is not None:
|
|
129
129
|
return r
|
|
130
130
|
return safe_null_result(op_str, target_str, engine=engine_str, reason="no-result")
|
|
131
|
-
except Exception:
|
|
131
|
+
except Exception as exc:
|
|
132
|
+
log_swallowed(f"Gateway._dispatch_single[{engine_str}.{op_str}]", exc)
|
|
132
133
|
return safe_null_result(op_str, target_str, engine=engine_str, reason="provider-error")
|
|
133
134
|
|
|
134
135
|
def query(
|
|
@@ -206,14 +207,15 @@ class Gateway:
|
|
|
206
207
|
provider = self._provider_for(engine_str)
|
|
207
208
|
result = self._dispatch_single(provider, op_str, target_str, budget, project_root, engine_str)
|
|
208
209
|
|
|
209
|
-
# overview auto-fallback (F4 Story 2): when auto-routed to graph but graph
|
|
210
|
-
# unavailable,
|
|
210
|
+
# overview auto-fallback (F4 Story 2): when auto-routed to graph but graph can't serve
|
|
211
|
+
# it — the backend is unavailable, OR this repo simply isn't in the graph — try lsp,
|
|
212
|
+
# which can produce a file/symbol overview without the graph index.
|
|
211
213
|
if (
|
|
212
214
|
was_auto
|
|
213
215
|
and op_str == "overview"
|
|
214
216
|
and engine_str == "graph"
|
|
215
217
|
and result.get("result") is None
|
|
216
|
-
and result.get("reason")
|
|
218
|
+
and result.get("reason") in ("engine-unavailable", "project-not-indexed")
|
|
217
219
|
):
|
|
218
220
|
lsp_result = self._dispatch_single(
|
|
219
221
|
self.lsp, op_str, target_str, budget, project_root, "lsp"
|
|
@@ -224,5 +226,6 @@ class Gateway:
|
|
|
224
226
|
self._cache.put(op_str, target_str, engine_str, root_str, result, freshness)
|
|
225
227
|
return result
|
|
226
228
|
|
|
227
|
-
except Exception:
|
|
229
|
+
except Exception as exc:
|
|
230
|
+
log_swallowed("Gateway.query", exc)
|
|
228
231
|
return safe_null_result(op or "", target or "", reason="gateway-error")
|