| 2338 | } |
| 2339 | |
| 2340 | static bool cbm_openclaw_workspace_path(const char *home_dir, const char *config_path, char *out, |
| 2341 | size_t out_sz) { |
| 2342 | char internal_home[CLI_BUF_1K]; |
| 2343 | if (!cbm_openclaw_internal_home(home_dir, internal_home, sizeof(internal_home))) { |
| 2344 | return false; |
| 2345 | } |
| 2346 | static const char *const defaults_path[] = {"agents", "defaults"}; |
| 2347 | char *configured = NULL; |
| 2348 | int lookup = |
| 2349 | cbm_json_like_get_string_at_path(config_path, defaults_path, 2U, "workspace", &configured); |
| 2350 | if (lookup == 0) { |
| 2351 | bool ok = cbm_expand_user_path(internal_home, configured, out, out_sz); |
| 2352 | free(configured); |
| 2353 | return ok; |
| 2354 | } |
| 2355 | free(configured); |
| 2356 | if (lookup < 0) { |
| 2357 | return false; |
| 2358 | } |
| 2359 | |
| 2360 | /* OpenClaw supports $include in its JSON5 config. Resolving and merging an |
| 2361 | * arbitrary include graph is outside the installer's safe mutation scope; |
| 2362 | * never guess the default workspace when an include may define it. */ |
| 2363 | static const char *const root_path[] = {NULL}; |
| 2364 | char *include_value = NULL; |
| 2365 | int include_lookup = |
| 2366 | cbm_json_like_get_string_at_path(config_path, root_path, 0U, "$include", &include_value); |
| 2367 | free(include_value); |
| 2368 | if (include_lookup != 1) { |
| 2369 | return false; |
| 2370 | } |
| 2371 | |
| 2372 | char env_buf[CLI_BUF_1K]; |
| 2373 | const char *workspace = |
| 2374 | cbm_safe_getenv("OPENCLAW_WORKSPACE_DIR", env_buf, sizeof(env_buf), NULL); |
| 2375 | if (workspace && workspace[0]) { |
| 2376 | return cbm_expand_user_path(internal_home, workspace, out, out_sz); |
| 2377 | } |
| 2378 | |
| 2379 | char profile_buf[CLI_BUF_256]; |
| 2380 | const char *profile = |
| 2381 | cbm_safe_getenv("OPENCLAW_PROFILE", profile_buf, sizeof(profile_buf), NULL); |
| 2382 | const char *suffix = ""; |
| 2383 | char profile_suffix[CLI_BUF_256] = {0}; |
| 2384 | if (profile && profile[0] && strcmp(profile, "default") != 0) { |
| 2385 | for (const unsigned char *p = (const unsigned char *)profile; *p; p++) { |
| 2386 | if (!isalnum(*p) && *p != '-' && *p != '_') { |
| 2387 | return false; |
| 2388 | } |
| 2389 | } |
| 2390 | int suffix_len = snprintf(profile_suffix, sizeof(profile_suffix), "-%s", profile); |
| 2391 | if (suffix_len < 0 || (size_t)suffix_len >= sizeof(profile_suffix)) { |
| 2392 | return false; |
| 2393 | } |
| 2394 | suffix = profile_suffix; |
| 2395 | } |
| 2396 | int written = snprintf(out, out_sz, "%s/.openclaw/workspace%s", internal_home, suffix); |
| 2397 | return written > 0 && (size_t)written < out_sz; |
no test coverage detected