mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-05-04 02:34:52 +00:00
various changes
This commit is contained in:
@@ -1,268 +1,268 @@
|
||||
#include "lua_runner.h"
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace lua {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct LuaRunner::Impl {
|
||||
sol::state lua;
|
||||
sol::table current_module;
|
||||
};
|
||||
|
||||
LuaRunner::LuaRunner(const fs::path& scripts_base)
|
||||
: impl_(std::make_unique<Impl>())
|
||||
, scripts_base_(scripts_base) {
|
||||
setup_sandbox();
|
||||
}
|
||||
|
||||
LuaRunner::~LuaRunner() = default;
|
||||
|
||||
void LuaRunner::setup_sandbox() {
|
||||
// Open only safe libraries
|
||||
impl_->lua.open_libraries(
|
||||
sol::lib::base,
|
||||
sol::lib::string,
|
||||
sol::lib::table,
|
||||
sol::lib::math,
|
||||
sol::lib::utf8
|
||||
);
|
||||
|
||||
// Remove dangerous functions from base
|
||||
impl_->lua["dofile"] = sol::lua_nil;
|
||||
impl_->lua["loadfile"] = sol::lua_nil;
|
||||
|
||||
// Custom print that captures output
|
||||
impl_->lua.set_function("print", [](sol::variadic_args args) {
|
||||
for (auto arg : args) {
|
||||
std::cout << arg.as<std::string>();
|
||||
std::cout << "\t";
|
||||
}
|
||||
std::cout << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
fs::path LuaRunner::find_module_path(const std::string& package_id, const std::string& module_name) {
|
||||
// Try: packages/{package_id}/seed/scripts/{module_name}/init.lua
|
||||
auto path1 = scripts_base_ / package_id / "seed" / "scripts" / module_name / "init.lua";
|
||||
if (fs::exists(path1)) return path1;
|
||||
|
||||
// Try: packages/{package_id}/seed/scripts/{module_name}.lua
|
||||
auto path2 = scripts_base_ / package_id / "seed" / "scripts" / (module_name + ".lua");
|
||||
if (fs::exists(path2)) return path2;
|
||||
|
||||
// Try: {package_id}/seed/scripts/{module_name}/init.lua (if scripts_base includes packages/)
|
||||
auto path3 = scripts_base_ / "packages" / package_id / "seed" / "scripts" / module_name / "init.lua";
|
||||
if (fs::exists(path3)) return path3;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LuaRunner::load_module(const std::string& package_id, const std::string& module_name) {
|
||||
auto module_path = find_module_path(package_id, module_name);
|
||||
|
||||
if (module_path.empty()) {
|
||||
last_error_ = "Module not found: " + package_id + "/" + module_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up package.path to include the scripts directory
|
||||
auto scripts_dir = module_path.parent_path();
|
||||
if (module_path.filename() == "init.lua") {
|
||||
scripts_dir = scripts_dir.parent_path();
|
||||
}
|
||||
|
||||
std::string package_path = impl_->lua["package"]["path"].get<std::string>();
|
||||
package_path += ";" + scripts_dir.string() + "/?.lua";
|
||||
package_path += ";" + scripts_dir.string() + "/?/init.lua";
|
||||
impl_->lua["package"]["path"] = package_path;
|
||||
|
||||
try {
|
||||
auto result = impl_->lua.safe_script_file(module_path.string());
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
last_error_ = err.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
impl_->current_module = result.get<sol::table>();
|
||||
module_loaded_ = true;
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
last_error_ = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Convert LuaConfig to sol::table
|
||||
sol::table config_to_lua(sol::state& lua, const LuaConfig& config) {
|
||||
sol::table tbl = lua.create_table();
|
||||
|
||||
for (const auto& [key, value] : config) {
|
||||
std::visit([&](auto&& v) {
|
||||
using T = std::decay_t<decltype(v)>;
|
||||
if constexpr (std::is_same_v<T, std::nullptr_t>) {
|
||||
tbl[key] = sol::lua_nil;
|
||||
} else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
|
||||
sol::table arr = lua.create_table();
|
||||
int i = 1;
|
||||
for (const auto& s : v) {
|
||||
arr[i++] = s;
|
||||
}
|
||||
tbl[key] = arr;
|
||||
} else if constexpr (std::is_same_v<T, std::unordered_map<std::string, std::string>>) {
|
||||
sol::table map = lua.create_table();
|
||||
for (const auto& [k, val] : v) {
|
||||
map[k] = val;
|
||||
}
|
||||
tbl[key] = map;
|
||||
} else {
|
||||
tbl[key] = v;
|
||||
}
|
||||
}, value);
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RunResult LuaRunner::call(const std::string& func_name, const LuaConfig& config) {
|
||||
RunResult result;
|
||||
|
||||
if (!module_loaded_) {
|
||||
result.error = "No module loaded";
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::function func = impl_->current_module[func_name];
|
||||
if (!func.valid()) {
|
||||
result.error = "Function not found: " + func_name;
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
auto lua_config = config_to_lua(impl_->lua, config);
|
||||
sol::protected_function_result call_result = func(lua_config);
|
||||
|
||||
if (!call_result.valid()) {
|
||||
sol::error err = call_result;
|
||||
result.error = err.what();
|
||||
return result;
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
|
||||
// Try to extract output/files from result
|
||||
if (call_result.get_type() == sol::type::table) {
|
||||
sol::table tbl = call_result;
|
||||
|
||||
if (tbl["success"].valid()) {
|
||||
result.success = tbl["success"].get<bool>();
|
||||
}
|
||||
if (tbl["output"].valid()) {
|
||||
result.output = tbl["output"].get<std::string>();
|
||||
}
|
||||
if (tbl["error"].valid()) {
|
||||
result.error = tbl["error"].get<std::string>();
|
||||
}
|
||||
if (tbl["files"].valid()) {
|
||||
sol::table files = tbl["files"];
|
||||
for (auto& pair : files) {
|
||||
sol::table file = pair.second;
|
||||
GeneratedFile gf;
|
||||
gf.path = file["path"].get<std::string>();
|
||||
gf.content = file["content"].get<std::string>();
|
||||
result.files.push_back(std::move(gf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
result.error = e.what();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
RunResult LuaRunner::call(const std::string& func_name) {
|
||||
return call(func_name, {});
|
||||
}
|
||||
|
||||
ValidationResult LuaRunner::validate(const std::string& func_name, const LuaConfig& config) {
|
||||
ValidationResult result;
|
||||
|
||||
if (!module_loaded_) {
|
||||
result.errors.push_back("No module loaded");
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::function func = impl_->current_module[func_name];
|
||||
if (!func.valid()) {
|
||||
result.errors.push_back("Function not found: " + func_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
auto lua_config = config_to_lua(impl_->lua, config);
|
||||
sol::protected_function_result call_result = func(lua_config);
|
||||
|
||||
if (!call_result.valid()) {
|
||||
sol::error err = call_result;
|
||||
result.errors.push_back(err.what());
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::table tbl = call_result;
|
||||
result.valid = tbl["valid"].get<bool>();
|
||||
|
||||
if (tbl["errors"].valid()) {
|
||||
sol::table errs = tbl["errors"];
|
||||
for (auto& pair : errs) {
|
||||
result.errors.push_back(pair.second.as<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
result.errors.push_back(e.what());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> LuaRunner::get_list(const std::string& func_name) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (!module_loaded_) return result;
|
||||
|
||||
sol::function func = impl_->current_module[func_name];
|
||||
if (!func.valid()) return result;
|
||||
|
||||
try {
|
||||
sol::protected_function_result call_result = func();
|
||||
if (!call_result.valid()) return result;
|
||||
|
||||
sol::table tbl = call_result;
|
||||
for (auto& pair : tbl) {
|
||||
result.push_back(pair.second.as<std::string>());
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<GeneratedFile> LuaRunner::get_files(const std::string& func_name, const LuaConfig& config) {
|
||||
auto result = call(func_name, config);
|
||||
return std::move(result.files);
|
||||
}
|
||||
|
||||
} // namespace lua
|
||||
#include "lua_runner.h"
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#include <sol/sol.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace lua {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
struct LuaRunner::Impl {
|
||||
sol::state lua;
|
||||
sol::table current_module;
|
||||
};
|
||||
|
||||
LuaRunner::LuaRunner(const fs::path& scripts_base)
|
||||
: impl_(std::make_unique<Impl>())
|
||||
, scripts_base_(scripts_base) {
|
||||
setup_sandbox();
|
||||
}
|
||||
|
||||
LuaRunner::~LuaRunner() = default;
|
||||
|
||||
void LuaRunner::setup_sandbox() {
|
||||
// Open only safe libraries
|
||||
impl_->lua.open_libraries(
|
||||
sol::lib::base,
|
||||
sol::lib::string,
|
||||
sol::lib::table,
|
||||
sol::lib::math,
|
||||
sol::lib::utf8
|
||||
);
|
||||
|
||||
// Remove dangerous functions from base
|
||||
impl_->lua["dofile"] = sol::lua_nil;
|
||||
impl_->lua["loadfile"] = sol::lua_nil;
|
||||
|
||||
// Custom print that captures output
|
||||
impl_->lua.set_function("print", [](sol::variadic_args args) {
|
||||
for (auto arg : args) {
|
||||
std::cout << arg.as<std::string>();
|
||||
std::cout << "\t";
|
||||
}
|
||||
std::cout << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
fs::path LuaRunner::find_module_path(const std::string& package_id, const std::string& module_name) {
|
||||
// Try: packages/{package_id}/seed/scripts/{module_name}/init.lua
|
||||
auto path1 = scripts_base_ / package_id / "seed" / "scripts" / module_name / "init.lua";
|
||||
if (fs::exists(path1)) return path1;
|
||||
|
||||
// Try: packages/{package_id}/seed/scripts/{module_name}.lua
|
||||
auto path2 = scripts_base_ / package_id / "seed" / "scripts" / (module_name + ".lua");
|
||||
if (fs::exists(path2)) return path2;
|
||||
|
||||
// Try: {package_id}/seed/scripts/{module_name}/init.lua (if scripts_base includes packages/)
|
||||
auto path3 = scripts_base_ / "packages" / package_id / "seed" / "scripts" / module_name / "init.lua";
|
||||
if (fs::exists(path3)) return path3;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LuaRunner::load_module(const std::string& package_id, const std::string& module_name) {
|
||||
auto module_path = find_module_path(package_id, module_name);
|
||||
|
||||
if (module_path.empty()) {
|
||||
last_error_ = "Module not found: " + package_id + "/" + module_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up package.path to include the scripts directory
|
||||
auto scripts_dir = module_path.parent_path();
|
||||
if (module_path.filename() == "init.lua") {
|
||||
scripts_dir = scripts_dir.parent_path();
|
||||
}
|
||||
|
||||
std::string package_path = impl_->lua["package"]["path"].get<std::string>();
|
||||
package_path += ";" + scripts_dir.string() + "/?.lua";
|
||||
package_path += ";" + scripts_dir.string() + "/?/init.lua";
|
||||
impl_->lua["package"]["path"] = package_path;
|
||||
|
||||
try {
|
||||
auto result = impl_->lua.safe_script_file(module_path.string());
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
last_error_ = err.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
impl_->current_module = result.get<sol::table>();
|
||||
module_loaded_ = true;
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
last_error_ = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Convert LuaConfig to sol::table
|
||||
sol::table config_to_lua(sol::state& lua, const LuaConfig& config) {
|
||||
sol::table tbl = lua.create_table();
|
||||
|
||||
for (const auto& [key, value] : config) {
|
||||
std::visit([&](auto&& v) {
|
||||
using T = std::decay_t<decltype(v)>;
|
||||
if constexpr (std::is_same_v<T, std::nullptr_t>) {
|
||||
tbl[key] = sol::lua_nil;
|
||||
} else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
|
||||
sol::table arr = lua.create_table();
|
||||
int i = 1;
|
||||
for (const auto& s : v) {
|
||||
arr[i++] = s;
|
||||
}
|
||||
tbl[key] = arr;
|
||||
} else if constexpr (std::is_same_v<T, std::unordered_map<std::string, std::string>>) {
|
||||
sol::table map = lua.create_table();
|
||||
for (const auto& [k, val] : v) {
|
||||
map[k] = val;
|
||||
}
|
||||
tbl[key] = map;
|
||||
} else {
|
||||
tbl[key] = v;
|
||||
}
|
||||
}, value);
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RunResult LuaRunner::call(const std::string& func_name, const LuaConfig& config) {
|
||||
RunResult result;
|
||||
|
||||
if (!module_loaded_) {
|
||||
result.error = "No module loaded";
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::function func = impl_->current_module[func_name];
|
||||
if (!func.valid()) {
|
||||
result.error = "Function not found: " + func_name;
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
auto lua_config = config_to_lua(impl_->lua, config);
|
||||
sol::protected_function_result call_result = func(lua_config);
|
||||
|
||||
if (!call_result.valid()) {
|
||||
sol::error err = call_result;
|
||||
result.error = err.what();
|
||||
return result;
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
|
||||
// Try to extract output/files from result
|
||||
if (call_result.get_type() == sol::type::table) {
|
||||
sol::table tbl = call_result;
|
||||
|
||||
if (tbl["success"].valid()) {
|
||||
result.success = tbl["success"].get<bool>();
|
||||
}
|
||||
if (tbl["output"].valid()) {
|
||||
result.output = tbl["output"].get<std::string>();
|
||||
}
|
||||
if (tbl["error"].valid()) {
|
||||
result.error = tbl["error"].get<std::string>();
|
||||
}
|
||||
if (tbl["files"].valid()) {
|
||||
sol::table files = tbl["files"];
|
||||
for (auto& pair : files) {
|
||||
sol::table file = pair.second;
|
||||
GeneratedFile gf;
|
||||
gf.path = file["path"].get<std::string>();
|
||||
gf.content = file["content"].get<std::string>();
|
||||
result.files.push_back(std::move(gf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
result.error = e.what();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
RunResult LuaRunner::call(const std::string& func_name) {
|
||||
return call(func_name, {});
|
||||
}
|
||||
|
||||
ValidationResult LuaRunner::validate(const std::string& func_name, const LuaConfig& config) {
|
||||
ValidationResult result;
|
||||
|
||||
if (!module_loaded_) {
|
||||
result.errors.push_back("No module loaded");
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::function func = impl_->current_module[func_name];
|
||||
if (!func.valid()) {
|
||||
result.errors.push_back("Function not found: " + func_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
auto lua_config = config_to_lua(impl_->lua, config);
|
||||
sol::protected_function_result call_result = func(lua_config);
|
||||
|
||||
if (!call_result.valid()) {
|
||||
sol::error err = call_result;
|
||||
result.errors.push_back(err.what());
|
||||
return result;
|
||||
}
|
||||
|
||||
sol::table tbl = call_result;
|
||||
result.valid = tbl["valid"].get<bool>();
|
||||
|
||||
if (tbl["errors"].valid()) {
|
||||
sol::table errs = tbl["errors"];
|
||||
for (auto& pair : errs) {
|
||||
result.errors.push_back(pair.second.as<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
result.errors.push_back(e.what());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> LuaRunner::get_list(const std::string& func_name) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (!module_loaded_) return result;
|
||||
|
||||
sol::function func = impl_->current_module[func_name];
|
||||
if (!func.valid()) return result;
|
||||
|
||||
try {
|
||||
sol::protected_function_result call_result = func();
|
||||
if (!call_result.valid()) return result;
|
||||
|
||||
sol::table tbl = call_result;
|
||||
for (auto& pair : tbl) {
|
||||
result.push_back(pair.second.as<std::string>());
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<GeneratedFile> LuaRunner::get_files(const std::string& func_name, const LuaConfig& config) {
|
||||
auto result = call(func_name, config);
|
||||
return std::move(result.files);
|
||||
}
|
||||
|
||||
} // namespace lua
|
||||
|
||||
+135
-135
@@ -1,135 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace lua {
|
||||
|
||||
/**
|
||||
* Lua value variant for passing data to/from Lua
|
||||
*/
|
||||
using LuaValue = std::variant<
|
||||
std::nullptr_t,
|
||||
bool,
|
||||
int64_t,
|
||||
double,
|
||||
std::string,
|
||||
std::vector<std::string>,
|
||||
std::unordered_map<std::string, std::string>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Configuration map for Lua function calls
|
||||
*/
|
||||
using LuaConfig = std::unordered_map<std::string, LuaValue>;
|
||||
|
||||
/**
|
||||
* Generated file from Lua script
|
||||
*/
|
||||
struct GeneratedFile {
|
||||
std::string path;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validation result from Lua
|
||||
*/
|
||||
struct ValidationResult {
|
||||
bool valid = false;
|
||||
std::vector<std::string> errors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Result from running a Lua script
|
||||
*/
|
||||
struct RunResult {
|
||||
bool success = false;
|
||||
std::string output;
|
||||
std::string error;
|
||||
std::vector<GeneratedFile> files;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sandboxed Lua script runner
|
||||
*
|
||||
* Executes Lua scripts from MetaBuilder packages in a secure sandbox
|
||||
* that prevents access to os, io, debug, and other dangerous modules.
|
||||
*/
|
||||
class LuaRunner {
|
||||
public:
|
||||
/**
|
||||
* Create runner with base scripts path
|
||||
* @param scripts_base Base path to search for scripts (e.g., packages/)
|
||||
*/
|
||||
explicit LuaRunner(const fs::path& scripts_base);
|
||||
~LuaRunner();
|
||||
|
||||
// Non-copyable
|
||||
LuaRunner(const LuaRunner&) = delete;
|
||||
LuaRunner& operator=(const LuaRunner&) = delete;
|
||||
|
||||
/**
|
||||
* Load a module from a package
|
||||
* @param package_id Package containing the module
|
||||
* @param module_name Module name (e.g., "package_template")
|
||||
* @return true if loaded successfully
|
||||
*/
|
||||
bool load_module(const std::string& package_id, const std::string& module_name);
|
||||
|
||||
/**
|
||||
* Call a Lua function with config
|
||||
* @param func_name Function name (e.g., "generate")
|
||||
* @param config Configuration to pass
|
||||
* @return Result of the call
|
||||
*/
|
||||
RunResult call(const std::string& func_name, const LuaConfig& config);
|
||||
|
||||
/**
|
||||
* Call a Lua function without arguments
|
||||
*/
|
||||
RunResult call(const std::string& func_name);
|
||||
|
||||
/**
|
||||
* Get validation result from Lua
|
||||
*/
|
||||
ValidationResult validate(const std::string& func_name, const LuaConfig& config);
|
||||
|
||||
/**
|
||||
* Get list of strings from Lua function
|
||||
*/
|
||||
std::vector<std::string> get_list(const std::string& func_name);
|
||||
|
||||
/**
|
||||
* Get generated files from Lua function
|
||||
*/
|
||||
std::vector<GeneratedFile> get_files(const std::string& func_name, const LuaConfig& config);
|
||||
|
||||
/**
|
||||
* Get last error message
|
||||
*/
|
||||
const std::string& last_error() const { return last_error_; }
|
||||
|
||||
/**
|
||||
* Check if a module is loaded
|
||||
*/
|
||||
bool is_module_loaded() const { return module_loaded_; }
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
fs::path scripts_base_;
|
||||
std::string last_error_;
|
||||
bool module_loaded_ = false;
|
||||
|
||||
void setup_sandbox();
|
||||
fs::path find_module_path(const std::string& package_id, const std::string& module_name);
|
||||
};
|
||||
|
||||
} // namespace lua
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace lua {
|
||||
|
||||
/**
|
||||
* Lua value variant for passing data to/from Lua
|
||||
*/
|
||||
using LuaValue = std::variant<
|
||||
std::nullptr_t,
|
||||
bool,
|
||||
int64_t,
|
||||
double,
|
||||
std::string,
|
||||
std::vector<std::string>,
|
||||
std::unordered_map<std::string, std::string>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Configuration map for Lua function calls
|
||||
*/
|
||||
using LuaConfig = std::unordered_map<std::string, LuaValue>;
|
||||
|
||||
/**
|
||||
* Generated file from Lua script
|
||||
*/
|
||||
struct GeneratedFile {
|
||||
std::string path;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validation result from Lua
|
||||
*/
|
||||
struct ValidationResult {
|
||||
bool valid = false;
|
||||
std::vector<std::string> errors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Result from running a Lua script
|
||||
*/
|
||||
struct RunResult {
|
||||
bool success = false;
|
||||
std::string output;
|
||||
std::string error;
|
||||
std::vector<GeneratedFile> files;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sandboxed Lua script runner
|
||||
*
|
||||
* Executes Lua scripts from MetaBuilder packages in a secure sandbox
|
||||
* that prevents access to os, io, debug, and other dangerous modules.
|
||||
*/
|
||||
class LuaRunner {
|
||||
public:
|
||||
/**
|
||||
* Create runner with base scripts path
|
||||
* @param scripts_base Base path to search for scripts (e.g., packages/)
|
||||
*/
|
||||
explicit LuaRunner(const fs::path& scripts_base);
|
||||
~LuaRunner();
|
||||
|
||||
// Non-copyable
|
||||
LuaRunner(const LuaRunner&) = delete;
|
||||
LuaRunner& operator=(const LuaRunner&) = delete;
|
||||
|
||||
/**
|
||||
* Load a module from a package
|
||||
* @param package_id Package containing the module
|
||||
* @param module_name Module name (e.g., "package_template")
|
||||
* @return true if loaded successfully
|
||||
*/
|
||||
bool load_module(const std::string& package_id, const std::string& module_name);
|
||||
|
||||
/**
|
||||
* Call a Lua function with config
|
||||
* @param func_name Function name (e.g., "generate")
|
||||
* @param config Configuration to pass
|
||||
* @return Result of the call
|
||||
*/
|
||||
RunResult call(const std::string& func_name, const LuaConfig& config);
|
||||
|
||||
/**
|
||||
* Call a Lua function without arguments
|
||||
*/
|
||||
RunResult call(const std::string& func_name);
|
||||
|
||||
/**
|
||||
* Get validation result from Lua
|
||||
*/
|
||||
ValidationResult validate(const std::string& func_name, const LuaConfig& config);
|
||||
|
||||
/**
|
||||
* Get list of strings from Lua function
|
||||
*/
|
||||
std::vector<std::string> get_list(const std::string& func_name);
|
||||
|
||||
/**
|
||||
* Get generated files from Lua function
|
||||
*/
|
||||
std::vector<GeneratedFile> get_files(const std::string& func_name, const LuaConfig& config);
|
||||
|
||||
/**
|
||||
* Get last error message
|
||||
*/
|
||||
const std::string& last_error() const { return last_error_; }
|
||||
|
||||
/**
|
||||
* Check if a module is loaded
|
||||
*/
|
||||
bool is_module_loaded() const { return module_loaded_; }
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
fs::path scripts_base_;
|
||||
std::string last_error_;
|
||||
bool module_loaded_ = false;
|
||||
|
||||
void setup_sandbox();
|
||||
fs::path find_module_path(const std::string& package_id, const std::string& module_name);
|
||||
};
|
||||
|
||||
} // namespace lua
|
||||
|
||||
Reference in New Issue
Block a user