6 Commits

Author SHA1 Message Date
JunZ 3ce9fb5dc9 fix(client): crash-aware restart of a daemon that vanished mid-session (#214)
* fix(client): crash-aware restart of a daemon that vanished mid-session

A ConnectionRefusedError after the daemon was already ensured used to be
surfaced as an error, even though a vanished daemon is usually restartable
(manual ccc daemon stop, and soon idle-exit). But blindly restarting would
silently mask daemon crashes and could relaunch a crash-looping daemon
forever. Distinguish the two:

- The daemon's graceful shutdown path (StopRequest, SIGTERM/SIGINT) now
  writes a last_exit marker file {pid, reason, timestamp} in the runtime
  dir, removed again at the next daemon startup. A crashed daemon (SIGKILL,
  OOM, segfault) never writes it.
- HandshakeResponse carries the daemon's pid, so the client can match the
  marker against the exact process it was talking to — race-free, no
  wall-clock comparisons.
- On a vanished ensured daemon: marker with matching pid → graceful exit →
  silent transparent restart. No marker / pid mismatch → crash → restart
  once but warn on stderr pointing at the daemon log; after 3 consecutive
  crashes raise instead of relaunching. The streak resets whenever the
  daemon answers without needing a restart.

The fail-fast for a genuine version mismatch (binary swapped under us
mid-session) is kept unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JaFKwEvJXo7WQhZvrW5Qoe

* fix(tests): avoid signal.SIGKILL on Windows to satisfy mypy

signal.SIGKILL does not exist on Windows, failing the pre-commit mypy
check there. Guard on sys.platform and fall back to SIGTERM, which on
Windows means TerminateProcess — equally abrupt, so the crash-marker
assertion still holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SeERy25Ct8dMFMVxYSk51c

* refactor(client): use _HandshakeResult field accessors at call sites

Per review feedback on #214: carry the handshake result as one NamedTuple
and access .conn / .resp by name instead of tuple-unpacking, so field
reordering can't silently misalign call sites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 12:09:01 -07:00
Jiangzhou 92d611968e feat: unified Docker workspace mount with supervised daemon (#135)
* feat: unified Docker workspace mount with supervised daemon

Reshape the Docker experience around a single bind mount and a single
named volume. Global settings live on the host under
$HOME/.cocoindex_code/ (visible and editable); index data and the model
cache persist in one cocoindex-data volume; daemon runtime state stays
on the container's native filesystem.

CLI and MCP output now show host-side paths via a bidirectional
COCOINDEX_CODE_HOST_PATH_MAPPING translator. A shell wrapper that
forwards $PWD (COCOINDEX_CODE_HOST_CWD) lets ccc work from any project
subdirectory on the host.

The daemon tolerates a missing global_settings.yml (starts in
no-settings mode) so ccc init's interactive picker works in Docker on
first run. A supervisor restart loop in the entrypoint, driven by a new
COCOINDEX_CODE_DAEMON_SUPERVISED contract, makes settings-change
auto-restart safe — editing global_settings.yml triggers an in-place
daemon respawn without taking the container down.

Linux ownership alignment via PUID/PGID, gosu privilege drop, and a
coco user baked into the image. Release workflow now publishes to both
Docker Hub (cocoindex/cocoindex-code) and GHCR
(ghcr.io/cocoindex-io/cocoindex-code).

Also:
- Merge cocoindex-db and cocoindex-model-cache into a single volume
- find_parent_with_marker requires .cocoindex_code/settings.yml, so a
  workspace-root global-only dir doesn't trigger nested-init warnings
- New pytest marker `docker_e2e` gates the Docker-backed E2E suite
  (excluded from default pytest runs)

* fix: mypy on Windows for POSIX-only os.getuid/getgid calls
2026-04-14 13:30:50 -07:00
Jiangzhou 5b6e3f5538 feat: make sentence-transformers optional; interactive ccc init (#132)
- Move `sentence-transformers` behind `[embeddings-local]` and `[default]`
  extras (via `cocoindex[sentence-transformers]`), so `pip install
  cocoindex-code` is LiteLLM-only. Closes #117.
- `ccc init` is now interactive when global settings don't exist: pick
  provider (sentence-transformers / litellm) and model via a
  questionary TUI. New `--litellm-model MODEL` flag skips prompts and
  is the non-TTY escape hatch for LiteLLM. Closes #70.
- Change the default sentence-transformers model from
  `all-MiniLM-L6-v2` to `Snowflake/snowflake-arctic-embed-xs`
  (lighter, better quality for code).
- Generated `global_settings.yml` now includes a `ccc doctor` reminder
  and commented-out env-var examples (OPENAI_API_KEY, GEMINI_API_KEY,
  ANTHROPIC_API_KEY, VOYAGE_API_KEY).
- Model test during init runs in the daemon via the existing
  `DoctorRequest` path; the daemon loads the model once and stays
  running, so the user's next `ccc index` starts warm.
- Docker image now installs `cocoindex-code[default]` and pre-caches
  the new default model. The `COCOINDEX_CODE_EMBEDDING_MODEL` env var
  is no longer documented for Docker; users mount a
  `global_settings.yml` or pass `--litellm-model`.
- Extract `check_embedding` + `EmbeddingCheckResult` into `shared.py`;
  refactor daemon `_check_model` to delegate. Error messages in doctor
  output now include the exception type name (strictly more
  informative).
- Tests switch to a lighter `paraphrase-MiniLM-L3-v2` model via a new
  `make_test_user_settings()` helper in `conftest.py`, leaving CI
  cache costs unchanged.
2026-04-13 22:25:17 -07:00
Jiangzhou 3066e02194 feat: handle setting file issues more gracefully (#114) 2026-03-21 10:31:14 -07:00
Jiangzhou dd5f7b6768 feat: revisit and simplify CLI-daemon connection model (#106)
* feat: revisit and simplify CLI-daemon connection model

- CLI-daemon connections are stateless now.
- Also stop caching project settings in memory. Much easier to pick up
  new version.

* fix: type annotation
2026-03-20 11:49:25 -07:00
Jiangzhou 9000f1df7e feat: preliminary implementation for daemon + CLI (#67) 2026-03-14 15:57:26 -07:00