One-shot CLI commands execute through the shared daemon, exactly like MCP * sessions and hooks: an active daemon (any starter) is recycled, an absent * one is spawned for this command — with a hint that `daemon start` removes * that per-command cost. Only supervised index workers stay in-process. */
| 1539 | * one is spawned for this command — with a hint that `daemon start` removes |
| 1540 | * that per-command cost. Only supervised index workers stay in-process. */ |
| 1541 | static char *main_local_cli_daemon_execute(const char *tool_name, const char *args_json) { |
| 1542 | cbm_daemon_ipc_endpoint_t *endpoint = cbm_daemon_bootstrap_endpoint_new(NULL); |
| 1543 | char executable_path[MAIN_PATH_CAP] = {0}; |
| 1544 | cbm_daemon_build_identity_t identity; |
| 1545 | bool prepared = |
| 1546 | endpoint && |
| 1547 | cbm_http_server_resolve_binary_path(NULL, executable_path, sizeof(executable_path)) && |
| 1548 | main_build_identity(&identity) == MAIN_BUILD_IDENTITY_OK; |
| 1549 | if (!prepared) { |
| 1550 | (void)fprintf(stderr, "error: daemon-backed CLI coordination could not be prepared\n"); |
| 1551 | cbm_daemon_ipc_endpoint_free(endpoint); |
| 1552 | return NULL; |
| 1553 | } |
| 1554 | cbm_daemon_bootstrap_config_t config = { |
| 1555 | .role = CBM_DAEMON_PROCESS_MCP_CLIENT, |
| 1556 | .endpoint = endpoint, |
| 1557 | .identity = &identity, |
| 1558 | .executable_path = executable_path, |
| 1559 | .connect_timeout_ms = MAIN_CONNECT_TIMEOUT_MS, |
| 1560 | .startup_timeout_ms = MAIN_MCP_STARTUP_TIMEOUT_MS, |
| 1561 | }; |
| 1562 | cbm_daemon_bootstrap_result_t bootstrap; |
| 1563 | cbm_daemon_bootstrap_status_t status = main_client_bootstrap_with_upgrade(&config, &bootstrap); |
| 1564 | cbm_daemon_ipc_endpoint_free(endpoint); |
| 1565 | if (status != CBM_DAEMON_BOOTSTRAP_CONNECTED || !bootstrap.client) { |
| 1566 | (void)fprintf(stderr, "error: %s\n", |
| 1567 | bootstrap.message[0] ? bootstrap.message |
| 1568 | : "no CBM daemon connection for CLI execution"); |
| 1569 | return NULL; |
| 1570 | } |
| 1571 | if (bootstrap.daemon_spawned) { |
| 1572 | (void)fprintf(stderr, "hint: this command started a temporary CBM daemon. " |
| 1573 | "`codebase-memory-mcp daemon start` keeps one warm and removes this " |
| 1574 | "startup cost from every CLI command.\n"); |
| 1575 | } |
| 1576 | char session_root[MAIN_PATH_CAP]; |
| 1577 | char allowed_root[MAIN_PATH_CAP]; |
| 1578 | const char *allowed_root_ptr = NULL; |
| 1579 | char *result = NULL; |
| 1580 | uint8_t *response = NULL; |
| 1581 | uint32_t response_length = 0; |
| 1582 | bool context_ok = |
| 1583 | main_session_context(NULL, session_root, allowed_root, &allowed_root_ptr) && |
| 1584 | main_set_client_context(bootstrap.client, session_root, CBM_MCP_TOOL_PROFILE_ALL, NULL, |
| 1585 | NULL, MAIN_CONNECT_TIMEOUT_MS); |
| 1586 | if (context_ok && |
| 1587 | cbm_daemon_application_client_tool(bootstrap.client, tool_name, args_json, &response, |
| 1588 | &response_length, MAIN_REQUEST_TIMEOUT_MS) == |
| 1589 | CBM_DAEMON_RUNTIME_APPLICATION_OK && |
| 1590 | response && response_length > 0) { |
| 1591 | result = malloc((size_t)response_length + 1U); |
| 1592 | if (result) { |
| 1593 | memcpy(result, response, response_length); |
| 1594 | result[response_length] = '\0'; |
| 1595 | } |
| 1596 | } |
| 1597 | free(response); |
| 1598 | if (!result) { |
no test coverage detected