cocoindex-code 0.2.5__tar.gz → 0.2.6__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.
Files changed (20) hide show
  1. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/PKG-INFO +2 -2
  2. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/pyproject.toml +1 -1
  3. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/_version.py +2 -2
  4. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/cli.py +138 -1
  5. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/client.py +40 -0
  6. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/daemon.py +166 -4
  7. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/protocol.py +30 -0
  8. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/.gitignore +0 -0
  9. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/LICENSE +0 -0
  10. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/README.md +0 -0
  11. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/__init__.py +0 -0
  12. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/__main__.py +0 -0
  13. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/config.py +0 -0
  14. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/indexer.py +0 -0
  15. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/project.py +0 -0
  16. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/query.py +0 -0
  17. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/schema.py +0 -0
  18. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/server.py +0 -0
  19. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/settings.py +0 -0
  20. {cocoindex_code-0.2.5 → cocoindex_code-0.2.6}/src/cocoindex_code/shared.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cocoindex-code
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: MCP server for indexing and querying codebases using CocoIndex
5
5
  Project-URL: Homepage, https://github.com/cocoindex-io/cocoindex-code
6
6
  Project-URL: Repository, https://github.com/cocoindex-io/cocoindex-code
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.11
20
- Requires-Dist: cocoindex[litellm]==1.0.0a35
20
+ Requires-Dist: cocoindex[litellm]==1.0.0a37
21
21
  Requires-Dist: einops>=0.8.2
22
22
  Requires-Dist: mcp>=1.0.0
23
23
  Requires-Dist: msgspec>=0.19.0
@@ -23,7 +23,7 @@ classifiers = [
23
23
 
24
24
  dependencies = [
25
25
  "mcp>=1.0.0",
26
- "cocoindex[litellm]==1.0.0a35",
26
+ "cocoindex[litellm]==1.0.0a37",
27
27
  "sentence-transformers>=2.2.0",
28
28
  "sqlite-vec>=0.1.0",
29
29
  "pydantic>=2.0.0",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.5'
32
- __version_tuple__ = version_tuple = (0, 2, 5)
31
+ __version__ = version = '0.2.6'
32
+ __version_tuple__ = version_tuple = (0, 2, 6)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
 
7
7
  import typer as _typer
8
8
 
9
- from .protocol import IndexingProgress, ProjectStatusResponse, SearchResponse
9
+ from .protocol import DoctorCheckResult, IndexingProgress, ProjectStatusResponse, SearchResponse
10
10
  from .settings import (
11
11
  default_project_settings,
12
12
  default_user_settings,
@@ -415,6 +415,143 @@ def reset(
415
415
  )
416
416
 
417
417
 
418
+ def _print_section(name: str) -> None:
419
+ import click as _click
420
+
421
+ _typer.echo()
422
+ _typer.echo(_click.style(f" {name}", bold=True))
423
+ _typer.echo(_click.style(f" {'─' * 38}", fg="bright_black"))
424
+
425
+
426
+ def _print_error(msg: str) -> None:
427
+ import click as _click
428
+
429
+ _typer.echo(_click.style(f" ERROR: {msg}", fg="red"), err=True)
430
+
431
+
432
+ def _print_doctor_result(result: DoctorCheckResult) -> None:
433
+ import click as _click
434
+
435
+ if result.name == "done":
436
+ return
437
+ if result.ok:
438
+ tag = _click.style("[OK]", fg="green", bold=True)
439
+ else:
440
+ tag = _click.style("[FAIL]", fg="red", bold=True)
441
+ _typer.echo(f"\n {tag} {result.name}")
442
+ for line in result.details:
443
+ _typer.echo(f" {line}")
444
+ for err in result.errors:
445
+ _typer.echo(_click.style(f" ERROR: {err}", fg="red"), err=True)
446
+
447
+
448
+ @app.command()
449
+ def doctor() -> None:
450
+ """Check system health and report issues."""
451
+ from . import client as _client
452
+ from .settings import (
453
+ load_project_settings as _load_project_settings,
454
+ )
455
+ from .settings import (
456
+ load_user_settings as _load_user_settings,
457
+ )
458
+ from .settings import (
459
+ project_settings_path as _project_settings_path,
460
+ )
461
+ from .settings import (
462
+ user_settings_path as _user_settings_path,
463
+ )
464
+
465
+ # --- 1. Global settings (local, no daemon needed) ---
466
+ _print_section("Global Settings")
467
+ settings_path = _user_settings_path()
468
+ _typer.echo(f" Settings: {settings_path}")
469
+ try:
470
+ user_settings = _load_user_settings()
471
+ emb = user_settings.embedding
472
+ device_str = f", device={emb.device}" if emb.device else ""
473
+ _typer.echo(f" Embedding: provider={emb.provider}, model={emb.model}{device_str}")
474
+ if user_settings.envs:
475
+ _typer.echo(
476
+ f" Env vars (from settings): {', '.join(sorted(user_settings.envs.keys()))}"
477
+ )
478
+ except (FileNotFoundError, ValueError) as e:
479
+ _print_error(str(e))
480
+
481
+ # --- 2. Connect to daemon (handshake with auto-start/restart) ---
482
+ _print_section("Daemon")
483
+ daemon_ok = False
484
+ try:
485
+ status = _client.daemon_status()
486
+ _typer.echo(f" Version: {status.version}")
487
+ _typer.echo(f" Uptime: {status.uptime_seconds:.1f}s")
488
+ _typer.echo(f" Loaded projects: {len(status.projects)}")
489
+ daemon_ok = True
490
+ except Exception as e:
491
+ _print_error(f"Cannot connect to daemon: {e}")
492
+ _typer.echo(" Remaining daemon-side checks will be skipped.")
493
+
494
+ # --- 3. Daemon environment (requires daemon) ---
495
+ if daemon_ok:
496
+ try:
497
+ env_resp = _client.daemon_env()
498
+ settings_keys = set(env_resp.settings_env_names)
499
+ other_keys = [k for k in env_resp.env_names if k not in settings_keys]
500
+ if other_keys:
501
+ _typer.echo(f" Other env vars in daemon: {', '.join(sorted(other_keys))}")
502
+ except Exception as e:
503
+ _print_error(f"Failed to get daemon env: {e}")
504
+
505
+ # --- 4. Model check (daemon-side, global — before project checks) ---
506
+ if daemon_ok:
507
+ try:
508
+ _client.doctor(
509
+ project_root=None,
510
+ on_result=_print_doctor_result,
511
+ )
512
+ except Exception as e:
513
+ _print_error(f"Model check failed: {e}")
514
+
515
+ # --- 5. Detect project ---
516
+ project_root = find_project_root(Path.cwd())
517
+
518
+ # --- 6. Project settings (local, no daemon needed) ---
519
+ if project_root is not None:
520
+ _print_section("Project Settings")
521
+ ps_path = _project_settings_path(project_root)
522
+ _typer.echo(f" Settings: {ps_path}")
523
+ try:
524
+ ps = _load_project_settings(project_root)
525
+ _typer.echo(f" Include patterns ({len(ps.include_patterns)}):")
526
+ _typer.echo(f" {', '.join(ps.include_patterns)}")
527
+ _typer.echo(f" Exclude patterns ({len(ps.exclude_patterns)}):")
528
+ _typer.echo(f" {', '.join(ps.exclude_patterns)}")
529
+ if ps.language_overrides:
530
+ _typer.echo(" Language overrides:")
531
+ for lo in ps.language_overrides:
532
+ _typer.echo(f" .{lo.ext} -> {lo.lang}")
533
+ except (FileNotFoundError, ValueError) as e:
534
+ _print_error(str(e))
535
+
536
+ # --- 7. Project daemon-side checks (file walk + index status) ---
537
+ if daemon_ok and project_root is not None:
538
+ try:
539
+ _client.doctor(
540
+ project_root=str(project_root),
541
+ on_result=_print_doctor_result,
542
+ )
543
+ except Exception as e:
544
+ _print_error(f"Project checks failed: {e}")
545
+
546
+ # --- 8. Log files ---
547
+ _print_section("Log Files")
548
+ from .daemon import daemon_dir as _daemon_dir
549
+
550
+ log_dir = _daemon_dir()
551
+ _typer.echo(f" Daemon logs: {log_dir / 'daemon.log'}")
552
+ _typer.echo(" Check logs above for further troubleshooting.")
553
+
554
+
418
555
  @app.command()
419
556
  def mcp() -> None:
420
557
  """Run as MCP server (stdio mode)."""
@@ -20,7 +20,12 @@ from pathlib import Path
20
20
  from ._version import __version__
21
21
  from .daemon import _connection_family, daemon_pid_path, daemon_socket_path
22
22
  from .protocol import (
23
+ DaemonEnvRequest,
24
+ DaemonEnvResponse,
23
25
  DaemonStatusResponse,
26
+ DoctorCheckResult,
27
+ DoctorRequest,
28
+ DoctorResponse,
24
29
  ErrorResponse,
25
30
  HandshakeRequest,
26
31
  HandshakeResponse,
@@ -259,6 +264,41 @@ def stop() -> StopResponse:
259
264
  return _send(StopRequest()) # type: ignore[return-value]
260
265
 
261
266
 
267
+ def daemon_env() -> DaemonEnvResponse:
268
+ """Get environment variable names from the daemon."""
269
+ return _send(DaemonEnvRequest()) # type: ignore[return-value]
270
+
271
+
272
+ def doctor(
273
+ project_root: str | None = None,
274
+ on_result: Callable[[DoctorCheckResult], None] | None = None,
275
+ ) -> list[DoctorCheckResult]:
276
+ """Run doctor checks via daemon, streaming results to on_result callback."""
277
+ conn = _connect_and_handshake()
278
+ try:
279
+ conn.send_bytes(encode_request(DoctorRequest(project_root=project_root)))
280
+ results: list[DoctorCheckResult] = []
281
+ while True:
282
+ try:
283
+ data = conn.recv_bytes()
284
+ except EOFError:
285
+ raise RuntimeError("Connection to daemon lost during doctor checks")
286
+ resp = decode_response(data)
287
+ if isinstance(resp, ErrorResponse):
288
+ raise RuntimeError(f"Daemon error: {resp.message}")
289
+ if isinstance(resp, DoctorResponse):
290
+ results.append(resp.result)
291
+ if on_result is not None:
292
+ on_result(resp.result)
293
+ if resp.final:
294
+ break
295
+ else:
296
+ raise RuntimeError(f"Unexpected response: {type(resp).__name__}")
297
+ return results
298
+ finally:
299
+ conn.close()
300
+
301
+
262
302
  # ---------------------------------------------------------------------------
263
303
  # Daemon lifecycle helpers
264
304
  # ---------------------------------------------------------------------------
@@ -17,9 +17,15 @@ from typing import Any
17
17
  from ._version import __version__
18
18
  from .project import Project
19
19
  from .protocol import (
20
+ DaemonEnvRequest,
21
+ DaemonEnvResponse,
20
22
  DaemonProjectInfo,
21
23
  DaemonStatusRequest,
22
24
  DaemonStatusResponse,
25
+ DoctorCheckResult,
26
+ DoctorRequest,
27
+ DoctorResponse,
28
+ DoctorStreamResponse,
23
29
  ErrorResponse,
24
30
  HandshakeRequest,
25
31
  HandshakeResponse,
@@ -151,6 +157,7 @@ async def handle_connection(
151
157
  start_time: float,
152
158
  on_shutdown: Callable[[], None],
153
159
  settings_mtime_us: int | None,
160
+ settings_env_names: list[str],
154
161
  ) -> None:
155
162
  """Handle a single client connection (per-request model).
156
163
 
@@ -186,7 +193,7 @@ async def handle_connection(
186
193
  data = await loop.run_in_executor(None, conn.recv_bytes)
187
194
  req = decode_request(data)
188
195
 
189
- result = await _dispatch(req, registry, start_time, on_shutdown)
196
+ result = await _dispatch(req, registry, start_time, on_shutdown, settings_env_names)
190
197
  if isinstance(result, AsyncIterator):
191
198
  try:
192
199
  async for resp in result:
@@ -231,17 +238,161 @@ async def _search_with_wait(
231
238
  yield ErrorResponse(message=str(e))
232
239
 
233
240
 
241
+ async def _handle_doctor(
242
+ req: DoctorRequest,
243
+ registry: ProjectRegistry,
244
+ ) -> AsyncIterator[DoctorStreamResponse]:
245
+ """Run doctor checks sequentially, yielding results as they complete.
246
+
247
+ When ``project_root`` is None, only the model check runs (global scope).
248
+ When ``project_root`` is set, only project-specific checks run (file walk + index status).
249
+ The CLI calls this twice — once without project, once with — so that global checks
250
+ appear before project settings in the output.
251
+ """
252
+ if req.project_root is None:
253
+ # Global-scope checks
254
+ yield DoctorResponse(result=await _check_model(registry._embedder))
255
+ else:
256
+ # Project-scope checks
257
+ yield DoctorResponse(result=await _check_file_walk(req.project_root))
258
+ yield DoctorResponse(result=await _check_index_status(req.project_root))
259
+
260
+ # Final marker
261
+ yield DoctorResponse(
262
+ result=DoctorCheckResult(name="done", ok=True, details=[], errors=[]),
263
+ final=True,
264
+ )
265
+
266
+
267
+ async def _check_model(embedder: Embedder) -> DoctorCheckResult:
268
+ """Test the embedding model by embedding a short string."""
269
+ try:
270
+ vec = await embedder.embed("hello world")
271
+ dim = len(vec)
272
+ return DoctorCheckResult(
273
+ name="Model Check",
274
+ ok=True,
275
+ details=[f"Embedding dimension: {dim}"],
276
+ errors=[],
277
+ )
278
+ except Exception as e:
279
+ return DoctorCheckResult(
280
+ name="Model Check",
281
+ ok=False,
282
+ details=[],
283
+ errors=[str(e)],
284
+ )
285
+
286
+
287
+ async def _check_file_walk(project_root_str: str) -> DoctorCheckResult:
288
+ """Walk project files and report counts + gitignore paths."""
289
+ from pathlib import PurePath
290
+
291
+ from cocoindex.resources.file import PatternFilePathMatcher
292
+
293
+ from .indexer import GitignoreAwareMatcher
294
+ from .settings import load_gitignore_spec, load_project_settings
295
+
296
+ project_root = Path(project_root_str)
297
+ try:
298
+ ps = load_project_settings(project_root)
299
+ except FileNotFoundError as e:
300
+ return DoctorCheckResult(name="File Walk", ok=False, details=[], errors=[str(e)])
301
+
302
+ gitignore_spec = load_gitignore_spec(project_root)
303
+ base_matcher = PatternFilePathMatcher(
304
+ included_patterns=ps.include_patterns,
305
+ excluded_patterns=ps.exclude_patterns,
306
+ )
307
+ matcher = GitignoreAwareMatcher(base_matcher, gitignore_spec, project_root)
308
+
309
+ counts_by_ext: dict[str, int] = {}
310
+ gitignore_dirs: list[str] = []
311
+ total = 0
312
+
313
+ def _walk() -> None:
314
+ nonlocal total
315
+ for dirpath_str, dirnames, filenames in os.walk(project_root):
316
+ dirpath = Path(dirpath_str)
317
+ rel_dir = PurePath(dirpath.relative_to(project_root))
318
+ if rel_dir != PurePath(".") and not matcher.is_dir_included(rel_dir):
319
+ dirnames.clear()
320
+ continue
321
+
322
+ if (dirpath / ".gitignore").is_file():
323
+ gitignore_dirs.append(str(rel_dir))
324
+
325
+ for fname in filenames:
326
+ rel_path = rel_dir / fname if rel_dir != PurePath(".") else PurePath(fname)
327
+ if matcher.is_file_included(rel_path):
328
+ total += 1
329
+ ext = PurePath(fname).suffix or "(no ext)"
330
+ counts_by_ext[ext] = counts_by_ext.get(ext, 0) + 1
331
+
332
+ await asyncio.get_event_loop().run_in_executor(None, _walk)
333
+
334
+ details = [f"Total matched files: {total}"]
335
+ for ext, count in sorted(counts_by_ext.items(), key=lambda x: -x[1]):
336
+ details.append(f" {ext}: {count}")
337
+ if gitignore_dirs:
338
+ details.append(f"Loaded .gitignore from: {', '.join(gitignore_dirs)}")
339
+
340
+ return DoctorCheckResult(name="File Walk", ok=True, details=details, errors=[])
341
+
342
+
343
+ async def _check_index_status(project_root_str: str) -> DoctorCheckResult:
344
+ """Check index status by querying target_sqlite.db directly."""
345
+ from cocoindex.connectors import sqlite as coco_sqlite
346
+
347
+ project_root = Path(project_root_str)
348
+ db_path = project_root / ".cocoindex_code" / "target_sqlite.db"
349
+ details = [f"Index: {db_path}"]
350
+
351
+ if not db_path.exists():
352
+ details.append("Index not created yet.")
353
+ return DoctorCheckResult(name="Index Status", ok=True, details=details, errors=[])
354
+
355
+ try:
356
+ conn = coco_sqlite.connect(str(db_path), load_vec=True)
357
+ try:
358
+ with conn.readonly() as db:
359
+ total_chunks = db.execute("SELECT COUNT(*) FROM code_chunks_vec").fetchone()[0]
360
+ file_rows = db.execute("SELECT DISTINCT file_path FROM code_chunks_vec").fetchall()
361
+ total_files = len(file_rows)
362
+ lang_rows = db.execute(
363
+ "SELECT language, COUNT(*) FROM code_chunks_vec GROUP BY language"
364
+ ).fetchall()
365
+ languages = {row[0]: row[1] for row in lang_rows}
366
+ finally:
367
+ conn.close()
368
+
369
+ details.append(f"Chunks: {total_chunks}")
370
+ details.append(f"Files: {total_files}")
371
+ if languages:
372
+ details.append("Languages:")
373
+ for lang, count in sorted(languages.items(), key=lambda x: -x[1]):
374
+ details.append(f" {lang}: {count} chunks")
375
+ return DoctorCheckResult(name="Index Status", ok=True, details=details, errors=[])
376
+ except Exception as e:
377
+ return DoctorCheckResult(name="Index Status", ok=False, details=details, errors=[str(e)])
378
+
379
+
234
380
  async def _dispatch(
235
381
  req: Request,
236
382
  registry: ProjectRegistry,
237
383
  start_time: float,
238
384
  on_shutdown: Callable[[], None],
239
- ) -> Response | AsyncIterator[IndexStreamResponse] | AsyncIterator[SearchStreamResponse]:
385
+ settings_env_names: list[str],
386
+ ) -> (
387
+ Response
388
+ | AsyncIterator[IndexStreamResponse]
389
+ | AsyncIterator[SearchStreamResponse]
390
+ | AsyncIterator[DoctorStreamResponse]
391
+ ):
240
392
  """Dispatch a request to the appropriate handler.
241
393
 
242
394
  Returns a single Response for most requests, or an AsyncIterator for
243
- streaming requests (IndexRequest, or SearchRequest when waiting for
244
- load-time indexing).
395
+ streaming requests (IndexRequest, SearchRequest when waiting, DoctorRequest).
245
396
  """
246
397
  try:
247
398
  if isinstance(req, IndexRequest):
@@ -289,6 +440,15 @@ async def _dispatch(
289
440
  on_shutdown()
290
441
  return StopResponse(ok=True)
291
442
 
443
+ if isinstance(req, DaemonEnvRequest):
444
+ return DaemonEnvResponse(
445
+ env_names=sorted(os.environ.keys()),
446
+ settings_env_names=settings_env_names,
447
+ )
448
+
449
+ if isinstance(req, DoctorRequest):
450
+ return _handle_doctor(req, registry)
451
+
292
452
  return ErrorResponse(message=f"Unknown request type: {type(req).__name__}")
293
453
  except Exception as e:
294
454
  logger.exception("Error dispatching request")
@@ -314,6 +474,7 @@ def run_daemon() -> None:
314
474
  settings_mtime_us = global_settings_mtime_us()
315
475
 
316
476
  # Set environment variables from settings
477
+ settings_env_keys = list(user_settings.envs.keys())
317
478
  for key, value in user_settings.envs.items():
318
479
  os.environ[key] = value
319
480
 
@@ -363,6 +524,7 @@ def run_daemon() -> None:
363
524
  start_time,
364
525
  _request_shutdown,
365
526
  settings_mtime_us,
527
+ settings_env_keys,
366
528
  )
367
529
  )
368
530
  tasks.add(task)
@@ -42,6 +42,14 @@ class StopRequest(_msgspec.Struct, tag="stop"):
42
42
  pass
43
43
 
44
44
 
45
+ class DoctorRequest(_msgspec.Struct, tag="doctor"):
46
+ project_root: str | None = None
47
+
48
+
49
+ class DaemonEnvRequest(_msgspec.Struct, tag="daemon_env"):
50
+ pass
51
+
52
+
45
53
  Request = (
46
54
  HandshakeRequest
47
55
  | IndexRequest
@@ -50,6 +58,8 @@ Request = (
50
58
  | DaemonStatusRequest
51
59
  | RemoveProjectRequest
52
60
  | StopRequest
61
+ | DoctorRequest
62
+ | DaemonEnvRequest
53
63
  )
54
64
 
55
65
  # ---------------------------------------------------------------------------
@@ -136,6 +146,23 @@ class StopResponse(_msgspec.Struct, tag="stop"):
136
146
  ok: bool
137
147
 
138
148
 
149
+ class DoctorCheckResult(_msgspec.Struct):
150
+ name: str
151
+ ok: bool
152
+ details: list[str]
153
+ errors: list[str]
154
+
155
+
156
+ class DoctorResponse(_msgspec.Struct, tag="doctor"):
157
+ result: DoctorCheckResult
158
+ final: bool = False
159
+
160
+
161
+ class DaemonEnvResponse(_msgspec.Struct, tag="daemon_env"):
162
+ env_names: list[str]
163
+ settings_env_names: list[str]
164
+
165
+
139
166
  class ErrorResponse(_msgspec.Struct, tag="error"):
140
167
  message: str
141
168
 
@@ -150,11 +177,14 @@ Response = (
150
177
  | DaemonStatusResponse
151
178
  | RemoveProjectResponse
152
179
  | StopResponse
180
+ | DoctorResponse
181
+ | DaemonEnvResponse
153
182
  | ErrorResponse
154
183
  )
155
184
 
156
185
  IndexStreamResponse = IndexProgressUpdate | IndexWaitingNotice | IndexResponse | ErrorResponse
157
186
  SearchStreamResponse = IndexWaitingNotice | SearchResponse | ErrorResponse
187
+ DoctorStreamResponse = DoctorResponse | ErrorResponse
158
188
 
159
189
  # ---------------------------------------------------------------------------
160
190
  # Encode / decode helpers (msgpack binary)
File without changes
File without changes