@@ -46,6 +46,7 @@ struct GlobalConfig {
4646 // From config.toml [xlings]
4747 std::string xlingsBinaryMode; // "bundled" | "system" | absolute path
4848 std::filesystem::path xlingsHomeOverride; // empty = use registryDir
49+ std::string xlingsMirror = " CN" ; // "CN" | "GLOBAL" | empty = xlings default
4950
5051 // From config.toml [index]
5152 std::string defaultIndex; // "mcpplibs"
@@ -174,6 +175,30 @@ void write_file(const std::filesystem::path& p, std::string_view content) {
174175 os << content;
175176}
176177
178+ std::string normalize_xlings_mirror (std::string mirror) {
179+ for (char & ch : mirror) {
180+ if (ch >= ' a' && ch <= ' z' ) ch = static_cast <char >(ch - ' a' + ' A' );
181+ }
182+ return mirror;
183+ }
184+
185+ std::optional<std::string> read_xlings_json_mirror (const std::filesystem::path& path) {
186+ std::ifstream is (path);
187+ if (!is) return std::nullopt ;
188+ std::stringstream ss;
189+ ss << is.rdbuf ();
190+ auto content = ss.str ();
191+ auto key = content.find (" \" mirror\" " );
192+ if (key == std::string::npos) return std::nullopt ;
193+ auto colon = content.find (' :' , key);
194+ if (colon == std::string::npos) return std::nullopt ;
195+ auto first = content.find (' "' , colon + 1 );
196+ if (first == std::string::npos) return std::nullopt ;
197+ auto second = content.find (' "' , first + 1 );
198+ if (second == std::string::npos) return std::nullopt ;
199+ return content.substr (first + 1 , second - first - 1 );
200+ }
201+
177202bool write_default_config_toml (const std::filesystem::path& path) {
178203 constexpr auto tmpl = R"( # mcpp global config — auto-generated; safe to edit.
179204
@@ -182,6 +207,8 @@ bool write_default_config_toml(const std::filesystem::path& path) {
182207binary = "bundled"
183208# home: empty = use $MCPP_HOME/registry; can override
184209home = ""
210+ # mirror: "CN" | "GLOBAL" | "" (defer to xlings)
211+ mirror = "CN"
185212
186213[index]
187214default = "mcpplibs"
@@ -202,7 +229,8 @@ default_backend = "ninja"
202229}
203230
204231bool write_default_xlings_json (const std::filesystem::path& path,
205- const std::vector<IndexRepo>& repos)
232+ const std::vector<IndexRepo>& repos,
233+ std::string_view mirror)
206234{
207235 // Delegate to xlings module. Convert IndexRepo vec to pair span.
208236 std::vector<std::pair<std::string,std::string>> pairs;
@@ -212,7 +240,7 @@ bool write_default_xlings_json(const std::filesystem::path& path,
212240 // construct a temporary Env with home = path.parent_path().
213241 mcpp::xlings::Env env;
214242 env.home = path.parent_path ();
215- mcpp::xlings::seed_xlings_json (env, pairs);
243+ mcpp::xlings::seed_xlings_json (env, pairs, mirror );
216244 return std::filesystem::exists (path);
217245}
218246
@@ -356,6 +384,15 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
356384 cfg.xlingsBinaryMode = doc->get_string (" xlings.binary" ).value_or (" bundled" );
357385 if (auto h = doc->get_string (" xlings.home" ); h && !h->empty ())
358386 cfg.xlingsHomeOverride = *h;
387+ cfg.xlingsMirror = normalize_xlings_mirror (
388+ doc->get_string (" xlings.mirror" ).value_or (" CN" ));
389+ if (!cfg.xlingsMirror .empty () &&
390+ cfg.xlingsMirror != " CN" &&
391+ cfg.xlingsMirror != " GLOBAL" ) {
392+ return std::unexpected (ConfigError{
393+ std::format (" invalid xlings.mirror '{}': expected CN, GLOBAL, or empty" ,
394+ cfg.xlingsMirror )});
395+ }
359396 cfg.defaultIndex = doc->get_string (" index.default" ).value_or (" mcpplibs" );
360397 cfg.searchTtlSeconds = doc->get_int (" cache.search_ttl_seconds" ).value_or (3600 );
361398 cfg.defaultJobs = doc->get_int (" build.default_jobs" ).value_or (0 );
@@ -387,7 +424,7 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
387424 // 5. Seed registry/.xlings.json if missing
388425 auto xjson = cfg.xlingsHome () / " .xlings.json" ;
389426 if (!std::filesystem::exists (xjson)) {
390- write_default_xlings_json (xjson, cfg.indexRepos );
427+ write_default_xlings_json (xjson, cfg.indexRepos , cfg. xlingsMirror );
391428 }
392429
393430 // 6. Acquire xlings binary if needed
@@ -408,6 +445,15 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
408445 " configured xlings binary not found: {}" , cfg.xlingsBinary .string ())});
409446 }
410447
448+ if (!cfg.xlingsMirror .empty () &&
449+ read_xlings_json_mirror (xjson).value_or (" " ) != cfg.xlingsMirror ) {
450+ auto rc = mcpp::xlings::set_mirror (make_xlings_env (cfg), cfg.xlingsMirror , true );
451+ if (rc != 0 && !quiet) {
452+ std::println (stderr,
453+ " warning: failed to set xlings mirror to '{}'" , cfg.xlingsMirror );
454+ }
455+ }
456+
411457 // 7. Sandbox bootstrap (mcpp self-contained xlings environment).
412458 // Order matters:
413459 // a. Mirror xlings binary into sandbox so shim creation works.
@@ -429,6 +475,8 @@ void print_env(const GlobalConfig& cfg) {
429475 std::println (" MCPP_HOME = {}" , cfg.mcppHome .string ());
430476 std::println (" xlings binary = {}" , cfg.xlingsBinary .string ());
431477 std::println (" xlings home = {}" , cfg.xlingsHome ().string ());
478+ std::println (" xlings mirror = {}" ,
479+ cfg.xlingsMirror .empty () ? " (xlings default)" : cfg.xlingsMirror );
432480 std::println (" config = {}" , cfg.configFile .string ());
433481 std::println (" BMI cache = {}" , cfg.bmiCacheDir .string ());
434482 std::println (" meta cache = {}" , cfg.metaCacheDir .string ());
0 commit comments