Skip to content

Commit 9d4ac2b

Browse files
fix(daemon): accept Unicode session roots on Windows
Signed-off-by: Ertan <ertan.kucukoglu@gmail.com>
1 parent 5fbab7b commit 9d4ac2b

3 files changed

Lines changed: 49 additions & 7 deletions

File tree

‎src/daemon/application.c‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,11 @@ static bool application_regular_db_exists(const char *project) {
374374
return stat(path, &status) == 0 && S_ISREG(status.st_mode);
375375
}
376376

377+
static bool application_canonical_directory_exists(const char *path) {
378+
cbm_path_info_t info = {0};
379+
return cbm_path_info_utf8(path, &info) == 0 && info.is_directory;
380+
}
381+
377382
static cbm_daemon_application_watch_t *application_find_watch_locked(
378383
cbm_daemon_application_t *application, const char *project) {
379384
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(
23542359
if (canonical && allowed_present) {
23552360
canonical = cbm_canonical_path(allowed, canonical_allowed, sizeof(canonical_allowed));
23562361
}
2357-
struct stat root_status;
2358-
canonical =
2359-
canonical && stat(canonical_root, &root_status) == 0 && S_ISDIR(root_status.st_mode);
2362+
canonical = canonical && application_canonical_directory_exists(canonical_root);
23602363
bool set =
23612364
canonical && cbm_mcp_server_set_session_context(session->mcp, canonical_root,
23622365
allowed_present ? canonical_allowed : NULL);
@@ -3369,9 +3372,8 @@ static int application_background_index(cbm_daemon_application_t *application,
33693372
return -1;
33703373
}
33713374
char canonical_root[APPLICATION_PATH_CAP];
3372-
struct stat root_status;
33733375
if (!cbm_canonical_path(root_path, canonical_root, sizeof(canonical_root)) ||
3374-
stat(canonical_root, &root_status) != 0 || !S_ISDIR(root_status.st_mode)) {
3376+
!application_canonical_directory_exists(canonical_root)) {
33753377
return -1;
33763378
}
33773379
yyjson_mut_doc *document = yyjson_mut_doc_new(NULL);

‎tests/test_daemon_application.c‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,45 @@ TEST(daemon_application_requires_immutable_explicit_context) {
714714
PASS();
715715
}
716716

717+
TEST(daemon_application_accepts_utf8_session_context_root) {
718+
char root[APP_TEST_PATH_CAP];
719+
snprintf(root, sizeof(root), "%s/cbm-app-context-caf\xC3\xA9-XXXXXX", cbm_tmpdir());
720+
bool root_ok = cbm_mkdtemp(root) != NULL;
721+
cbm_daemon_application_t *application = cbm_daemon_application_new(NULL);
722+
cbm_daemon_runtime_application_callbacks_t callbacks =
723+
cbm_daemon_application_runtime_callbacks(application);
724+
cbm_daemon_runtime_application_session_t *session = app_test_open(&callbacks, 311);
725+
uint8_t *context = NULL;
726+
uint32_t context_length = 0;
727+
uint8_t *response = NULL;
728+
uint32_t response_length = 0;
729+
bool context_ok = root_ok && app_test_context_request(root, root, &context, &context_length);
730+
cbm_daemon_runtime_application_status_t status =
731+
context_ok ? app_test_request(&callbacks, session, context, context_length, &response,
732+
&response_length)
733+
: CBM_DAEMON_RUNTIME_APPLICATION_TRANSPORT_ERROR;
734+
735+
free(context);
736+
free(response);
737+
if (session) {
738+
callbacks.session_close(callbacks.context, session);
739+
}
740+
bool stopped = application && cbm_daemon_application_shutdown(application, APP_TEST_TIMEOUT_MS);
741+
cbm_daemon_application_free(application);
742+
if (root_ok) {
743+
(void)cbm_rmdir(root);
744+
}
745+
746+
ASSERT_TRUE(root_ok);
747+
ASSERT_NOT_NULL(application);
748+
ASSERT_NOT_NULL(session);
749+
ASSERT_TRUE(context_ok);
750+
ASSERT_EQ(status, CBM_DAEMON_RUNTIME_APPLICATION_OK);
751+
ASSERT_EQ(response_length, 0);
752+
ASSERT_TRUE(stopped);
753+
PASS();
754+
}
755+
717756
TEST(daemon_application_mcp_notification_has_no_response) {
718757
cbm_daemon_application_t *application = cbm_daemon_application_new(NULL);
719758
cbm_daemon_runtime_application_callbacks_t callbacks =
@@ -5348,6 +5387,7 @@ SUITE(daemon_application) {
53485387
RUN_TEST(daemon_application_new_session_does_not_retain_initial_store);
53495388
RUN_TEST(daemon_application_request_cancel_is_scoped_to_exact_token);
53505389
RUN_TEST(daemon_application_requires_immutable_explicit_context);
5390+
RUN_TEST(daemon_application_accepts_utf8_session_context_root);
53515391
RUN_TEST(daemon_application_ui_config_updates_are_masked_and_serialized);
53525392
RUN_TEST(daemon_application_ui_config_rejects_noncanonical_frames);
53535393
RUN_TEST(daemon_application_ui_readiness_proof_is_generation_bound_before_context);

‎tests/windows/test_non_ascii_path.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,9 @@ def no_project_error(index_txt, repo, cache):
358358

359359

360360
def index_and_count(binary, repo, cache):
361-
"""Index `repo` into an isolated cache and return label-resolved counts."""
361+
"""Start in and index `repo`, then return label-resolved counts."""
362362
os.makedirs(cache, exist_ok=True)
363-
with McpServer(binary, cache_dir=cache) as s:
363+
with McpServer(binary, cache_dir=cache, cwd=repo) as s:
364364
s.initialize()
365365
resp = s.call_tool("index_repository", {"repo_path": repo}, timeout=180)
366366
index_txt, err = s.tool_text(resp)

0 commit comments

Comments
 (0)