| 2366 | } |
| 2367 | |
| 2368 | static bool cbm_openclaw_workspace_path(const char *home_dir, const char *config_path, char *out, |
| 2369 | size_t out_sz) { |
| 2370 | char internal_home[CLI_BUF_1K]; |
| 2371 | if (!cbm_openclaw_internal_home(home_dir, internal_home, sizeof(internal_home))) { |
| 2372 | return false; |
| 2373 | } |
| 2374 | static const char *const defaults_path[] = {"agents", "defaults"}; |
| 2375 | char *configured = NULL; |
| 2376 | int lookup = |
| 2377 | cbm_json_like_get_string_at_path(config_path, defaults_path, 2U, "workspace", &configured); |
| 2378 | if (lookup == 0) { |
| 2379 | bool ok = cbm_expand_user_path(internal_home, configured, out, out_sz); |
| 2380 | free(configured); |
| 2381 | return ok; |
| 2382 | } |
| 2383 | free(configured); |
| 2384 | if (lookup < 0) { |
| 2385 | return false; |
| 2386 | } |
| 2387 | |
| 2388 | /* OpenClaw supports $include in its JSON5 config. Resolving and merging an |
| 2389 | * arbitrary include graph is outside the installer's safe mutation scope; |
| 2390 | * never guess the default workspace when an include may define it. */ |
| 2391 | static const char *const root_path[] = {NULL}; |
| 2392 | char *include_value = NULL; |
| 2393 | int include_lookup = |
| 2394 | cbm_json_like_get_string_at_path(config_path, root_path, 0U, "$include", &include_value); |
| 2395 | free(include_value); |
| 2396 | if (include_lookup != 1) { |
| 2397 | return false; |
| 2398 | } |
| 2399 | |
| 2400 | char env_buf[CLI_BUF_1K]; |
| 2401 | const char *workspace = |
| 2402 | cbm_safe_getenv("OPENCLAW_WORKSPACE_DIR", env_buf, sizeof(env_buf), NULL); |
| 2403 | if (workspace && workspace[0]) { |
| 2404 | return cbm_expand_user_path(internal_home, workspace, out, out_sz); |
| 2405 | } |
| 2406 | |
| 2407 | char profile_buf[CLI_BUF_256]; |
| 2408 | const char *profile = |
| 2409 | cbm_safe_getenv("OPENCLAW_PROFILE", profile_buf, sizeof(profile_buf), NULL); |
| 2410 | const char *suffix = ""; |
| 2411 | char profile_suffix[CLI_BUF_256] = {0}; |
| 2412 | if (profile && profile[0] && strcmp(profile, "default") != 0) { |
| 2413 | for (const unsigned char *p = (const unsigned char *)profile; *p; p++) { |
| 2414 | if (!isalnum(*p) && *p != '-' && *p != '_') { |
| 2415 | return false; |
| 2416 | } |
| 2417 | } |
| 2418 | int suffix_len = snprintf(profile_suffix, sizeof(profile_suffix), "-%s", profile); |
| 2419 | if (suffix_len < 0 || (size_t)suffix_len >= sizeof(profile_suffix)) { |
| 2420 | return false; |
| 2421 | } |
| 2422 | suffix = profile_suffix; |
| 2423 | } |
| 2424 | int written = snprintf(out, out_sz, "%s/.openclaw/workspace%s", internal_home, suffix); |
| 2425 | return written > 0 && (size_t)written < out_sz; |
no test coverage detected