fix(daemon): accept Unicode session roots on Windows

Signed-off-by: Ertan <ertan.kucukoglu@gmail.com>
This commit is contained in:
Ertan committed 2026-09-02 01:00:31 +03:00
1 parent 5fbab7bb73
commit 9d4ac2b98b
3 files changed
+49 -7

No files matched your search

+7 -5
View File
@@ -374,6 +374,11 @@ static bool application_regular_db_exists(const char *project) {
return stat(path, &status) == 0 && S_ISREG(status.st_mode);
}
static bool application_canonical_directory_exists(const char *path) {
cbm_path_info_t info = {0};
return cbm_path_info_utf8(path, &info) == 0 && info.is_directory;
}
static cbm_daemon_application_watch_t *application_find_watch_locked(
cbm_daemon_application_t *application, const char *project) {
for (cbm_daemon_application_watch_t *watch = application->watches; watch; watch = watch->next) {
@@ -2354,9 +2359,7 @@ static cbm_daemon_runtime_application_status_t application_set_context(
if (canonical && allowed_present) {
canonical = cbm_canonical_path(allowed, canonical_allowed, sizeof(canonical_allowed));
}
struct stat root_status;
canonical =
canonical && stat(canonical_root, &root_status) == 0 && S_ISDIR(root_status.st_mode);
canonical = canonical && application_canonical_directory_exists(canonical_root);
bool set =
canonical && cbm_mcp_server_set_session_context(session->mcp, canonical_root,
allowed_present ? canonical_allowed : NULL);
@@ -3369,9 +3372,8 @@ static int application_background_index(cbm_daemon_application_t *application,
return -1;
}
char canonical_root[APPLICATION_PATH_CAP];
struct stat root_status;
if (!cbm_canonical_path(root_path, canonical_root, sizeof(canonical_root)) ||
stat(canonical_root, &root_status) != 0 || !S_ISDIR(root_status.st_mode)) {
!application_canonical_directory_exists(canonical_root)) {
return -1;
}
yyjson_mut_doc *document = yyjson_mut_doc_new(NULL);
+40
View File
@@ -714,6 +714,45 @@ TEST(daemon_application_requires_immutable_explicit_context) {
PASS();
}
TEST(daemon_application_accepts_utf8_session_context_root) {
char root[APP_TEST_PATH_CAP];
snprintf(root, sizeof(root), "%s/cbm-app-context-caf\xC3\xA9-XXXXXX", cbm_tmpdir());
bool root_ok = cbm_mkdtemp(root) != NULL;
cbm_daemon_application_t *application = cbm_daemon_application_new(NULL);
cbm_daemon_runtime_application_callbacks_t callbacks =
cbm_daemon_application_runtime_callbacks(application);
cbm_daemon_runtime_application_session_t *session = app_test_open(&callbacks, 311);
uint8_t *context = NULL;
uint32_t context_length = 0;
uint8_t *response = NULL;
uint32_t response_length = 0;
bool context_ok = root_ok && app_test_context_request(root, root, &context, &context_length);
cbm_daemon_runtime_application_status_t status =
context_ok ? app_test_request(&callbacks, session, context, context_length, &response,
&response_length)
: CBM_DAEMON_RUNTIME_APPLICATION_TRANSPORT_ERROR;
free(context);
free(response);
if (session) {
callbacks.session_close(callbacks.context, session);
}
bool stopped = application && cbm_daemon_application_shutdown(application, APP_TEST_TIMEOUT_MS);
cbm_daemon_application_free(application);
if (root_ok) {
(void)cbm_rmdir(root);
}
ASSERT_TRUE(root_ok);
ASSERT_NOT_NULL(application);
ASSERT_NOT_NULL(session);
ASSERT_TRUE(context_ok);
ASSERT_EQ(status, CBM_DAEMON_RUNTIME_APPLICATION_OK);
ASSERT_EQ(response_length, 0);
ASSERT_TRUE(stopped);
PASS();
}
TEST(daemon_application_mcp_notification_has_no_response) {
cbm_daemon_application_t *application = cbm_daemon_application_new(NULL);
cbm_daemon_runtime_application_callbacks_t callbacks =
@@ -5348,6 +5387,7 @@ SUITE(daemon_application) {
RUN_TEST(daemon_application_new_session_does_not_retain_initial_store);
RUN_TEST(daemon_application_request_cancel_is_scoped_to_exact_token);
RUN_TEST(daemon_application_requires_immutable_explicit_context);
RUN_TEST(daemon_application_accepts_utf8_session_context_root);
RUN_TEST(daemon_application_ui_config_updates_are_masked_and_serialized);
RUN_TEST(daemon_application_ui_config_rejects_noncanonical_frames);
RUN_TEST(daemon_application_ui_readiness_proof_is_generation_bound_before_context);
+2 -2
View File
@@ -358,9 +358,9 @@ def no_project_error(index_txt, repo, cache):
def index_and_count(binary, repo, cache):
"""Index `repo` into an isolated cache and return label-resolved counts."""
"""Start in and index `repo`, then return label-resolved counts."""
os.makedirs(cache, exist_ok=True)
with McpServer(binary, cache_dir=cache) as s:
with McpServer(binary, cache_dir=cache, cwd=repo) as s:
s.initialize()
resp = s.call_tool("index_repository", {"repo_path": repo}, timeout=180)
index_txt, err = s.tool_text(resp)