| 2519 | } |
| 2520 | |
| 2521 | static bool cbm_openclaw_workspace_path(const char *home_dir, const char *config_path, char *out, |
| 2522 | size_t out_sz) { |
| 2523 | char internal_home[CLI_BUF_1K]; |
| 2524 | if (!cbm_openclaw_internal_home(home_dir, internal_home, sizeof(internal_home))) { |
| 2525 | return false; |
| 2526 | } |
| 2527 | static const char *const defaults_path[] = {"agents", "defaults"}; |
| 2528 | char *configured = NULL; |
| 2529 | int lookup = |
| 2530 | cbm_json_like_get_string_at_path(config_path, defaults_path, 2U, "workspace", &configured); |
| 2531 | if (lookup == 0) { |
| 2532 | bool ok = cbm_expand_user_path(internal_home, configured, out, out_sz); |
| 2533 | free(configured); |
| 2534 | return ok; |
| 2535 | } |
| 2536 | free(configured); |
| 2537 | if (lookup < 0) { |
| 2538 | return false; |
| 2539 | } |
| 2540 | |
| 2541 | /* OpenClaw supports $include in its JSON5 config. Resolving and merging an |
| 2542 | * arbitrary include graph is outside the installer's safe mutation scope; |
| 2543 | * never guess the default workspace when an include may define it. */ |
| 2544 | static const char *const root_path[] = {NULL}; |
| 2545 | char *include_value = NULL; |
| 2546 | int include_lookup = |
| 2547 | cbm_json_like_get_string_at_path(config_path, root_path, 0U, "$include", &include_value); |
| 2548 | free(include_value); |
| 2549 | if (include_lookup != 1) { |
| 2550 | return false; |
| 2551 | } |
| 2552 | |
| 2553 | char env_buf[CLI_BUF_1K]; |
| 2554 | const char *workspace = |
| 2555 | cbm_safe_getenv("OPENCLAW_WORKSPACE_DIR", env_buf, sizeof(env_buf), NULL); |
| 2556 | if (workspace && workspace[0]) { |
| 2557 | return cbm_expand_user_path(internal_home, workspace, out, out_sz); |
| 2558 | } |
| 2559 | |
| 2560 | char profile_buf[CLI_BUF_256]; |
| 2561 | const char *profile = |
| 2562 | cbm_safe_getenv("OPENCLAW_PROFILE", profile_buf, sizeof(profile_buf), NULL); |
| 2563 | const char *suffix = ""; |
| 2564 | char profile_suffix[CLI_BUF_256] = {0}; |
| 2565 | if (profile && profile[0] && strcmp(profile, "default") != 0) { |
| 2566 | for (const unsigned char *p = (const unsigned char *)profile; *p; p++) { |
| 2567 | if (!isalnum(*p) && *p != '-' && *p != '_') { |
| 2568 | return false; |
| 2569 | } |
| 2570 | } |
| 2571 | int suffix_len = snprintf(profile_suffix, sizeof(profile_suffix), "-%s", profile); |
| 2572 | if (suffix_len < 0 || (size_t)suffix_len >= sizeof(profile_suffix)) { |
| 2573 | return false; |
| 2574 | } |
| 2575 | suffix = profile_suffix; |
| 2576 | } |
| 2577 | int written = snprintf(out, out_sz, "%s/.openclaw/workspace%s", internal_home, suffix); |
| 2578 | return written > 0 && (size_t)written < out_sz; |
no test coverage detected