-- pkg: Lightweight package manager for LeonOS -- 程序顶部名称栏 local term = require("term") local colors = require("colors") local fs = require("fs") local shell = require("shell") local textutils = require("textutils") local http = require("http") -- 保存当前颜色设置 local old_fg = term.getTextColor() local old_bg = term.getBackgroundColor() -- 设置名称栏颜色并显示 term.setTextColor(colors.white) term.setBackgroundColor(colors.cyan) term.at(1, 1).clearLine() term.at(1, 1).write("=== LeonOS Package Manager ===") -- 恢复颜色设置 term.setTextColor(old_fg) term.setBackgroundColor(old_bg) term.at(1, 2) -- 包管理器配置 local pkg_config = { repo_url = "https://example.com/leonos/packages", -- 包仓库URL local_pkg_dir = "/packages", -- 本地包存储目录 installed_db = "/packages/installed.json", -- 已安装包数据库 cache_dir = "/packages/cache", -- 缓存目录 github_api_url = "https://gh.catmak.name/https://raw.githubusercontent.com" -- GitHub API基础URL } -- 创建新包 local function create_package(pkg_name) print("Creating new package: " .. pkg_name) -- 确保包目录存在 local pkg_version = "1.0.0" local pkg_dir = fs.combine(pkg_config.local_pkg_dir, pkg_name) local version_dir = fs.combine(pkg_dir, pkg_version) if not fs.exists(pkg_dir) then fs.makeDir(pkg_dir) end if not fs.exists(version_dir) then fs.makeDir(version_dir) end -- 创建package.json文件 local package_json = { name = pkg_name, version = pkg_version, description = "A new package for LeonOS", author = "LeonOS User", license = "MIT", type = "app", -- 默认类型为app dependencies = {}, files = { pkg_name .. ".lua" } } local json_file = io.open(fs.combine(version_dir, "package.json"), "w") if json_file then json_file:write(textutils.serializeJSON(package_json, false)) json_file:close() print("Created package.json") else print("Error: Failed to create package.json") return false end -- 创建主代码文件 local main_file_path = fs.combine(version_dir, pkg_name .. ".lua") local main_file = io.open(main_file_path, "w") if main_file then local main_file_content = [[-- ]] .. pkg_name .. [[ Package local colors = require('colors') local term = require('term') function drawTopBar() local w, h = term.getSize() term.setBackgroundColor(colors.cyan) term.setTextColor(colors.white) term.setCursorPos(1, 1) term.clearLine() local title = "=== ]] .. pkg_name .. [[ v]] .. pkg_version .. [[ ===" local pos = math.floor((w - #title) / 2) + 1 term.setCursorPos(pos, 1) term.write(title) term.setBackgroundColor(colors.black) term.setTextColor(colors.white) term.setCursorPos(1, 3) end drawTopBar() print("\nThis is the ]] .. pkg_name .. [[ package for LeonOS.") print("\nUsage:") print(" pkg install ]] .. pkg_name .. [[ - Install this package") print(" pkg remove ]] .. pkg_name .. [[ - Uninstall this package") print(" pkg list - List installed packages") ]] main_file:write(main_file_content) main_file:close() print("Created " .. pkg_name .. ".lua") else print("Error: Failed to create " .. pkg_name .. ".lua") return false end print("Package created successfully at: " .. version_dir) print("You can now edit the package files and install it with 'pkg install ]] .. pkg_name .. [['") return true end -- 确保必要的目录存在 local function ensure_dirs() if not fs.exists(pkg_config.local_pkg_dir) then fs.makeDir(pkg_config.local_pkg_dir) end if not fs.exists(pkg_config.cache_dir) then fs.makeDir(pkg_config.cache_dir) end end -- 加载已安装的包数据库 local function load_installed_db() ensure_dirs() if fs.exists(pkg_config.installed_db) then local file = io.open(pkg_config.installed_db, "r") if file then local content = file:read("*a") file:close() local ok, data = pcall(textutils.unserializeJSON, content) if ok and data then return data end end end return { packages = {} } end -- 保存已安装的包数据库 local function save_installed_db(db) local file = io.open(pkg_config.installed_db, "w") if file then file:write(textutils.serializeJSON(db, false)) file:close() return true end return false end -- 显示帮助信息 local function show_help() print("Usage: pkg [options]") print("") print("Commands:") print(" install Install a package") print(" update Update a package (leave empty to update all)") print(" remove Remove a package") print(" list List all installed packages") print(" search Search for packages") print(" info Show package information") print(" init Create a new package") print(" help Show this help message") print("") print("Options:") print(" --local Install from local file") print(" --force Force install/update") end -- 从GitHub下载包元数据 local function download_github_package_meta(repo_path) print("Downloading metadata from GitHub: " .. repo_path) -- 检查http模块是否可用 if not http or not http.get then print("Error: HTTP module not available.") return nil end -- 构建GitHub API URL -- 假设package.json在仓库根目录 local meta_url = string.format("%s/%s/master/package.json", pkg_config.github_api_url, repo_path) -- 下载package.json local response, err = http.get(meta_url) if not response then print("Error downloading package.json: " .. err) return nil end -- 读取响应内容 local content = response.readAll() response.close() -- 解析JSON local ok, meta = pcall(textutils.unserializeJSON, content) if not ok or not meta then print("Error parsing package.json.") return nil end -- 添加仓库路径信息 meta.repo_path = repo_path return meta end -- 从GitHub下载包文件 local function download_github_package_file(repo_path, file_path, save_path) -- 构建文件下载URL local file_url = string.format("%s/%s/master/%s", pkg_config.github_api_url, repo_path, file_path) -- 下载文件 local response, err = http.get(file_url) if not response then print("Error downloading file: " .. file_path .. " - " .. err) return false end -- 确保保存目录存在 local save_dir = fs.getDir(save_path) if not fs.exists(save_dir) then fs.makeDir(save_dir) end -- 写入文件 local file = io.open(save_path, "w") if not file then print("Error creating file: " .. save_path) response.close() return false end file:write(response.readAll()) file:close() response.close() return true end -- 安装GitHub包 local function install_github_package(repo_path, options) print("Installing GitHub package: " .. repo_path) ensure_dirs() -- 下载包元数据 local meta = download_github_package_meta(repo_path) if not meta then return false end -- 解析包名 (从repo_path中提取或使用package.json中的name) local pkg_name = meta.name or string.match(repo_path, "([^/]+)$") if not pkg_name then print("Error: Cannot determine package name.") return false end local installed_db = load_installed_db() or { packages = {} } -- 检查是否已安装 if installed_db.packages[pkg_name] and not options.force then print("Package already installed. Use --force to reinstall.") return false end -- 创建包的本地目录结构 local pkg_version = meta.version or "1.0.0" local pkg_dir = fs.combine(pkg_config.local_pkg_dir, pkg_name) local version_dir = fs.combine(pkg_dir, pkg_version) if not fs.exists(pkg_dir) then fs.makeDir(pkg_dir) end if not fs.exists(version_dir) then fs.makeDir(version_dir) end -- 保存package.json到本地 local meta_path = fs.combine(version_dir, "package.json") local meta_file = io.open(meta_path, "w") if not meta_file then print("Error: Failed to save package.json") return false end meta_file:write(textutils.serializeJSON(meta, false)) meta_file:close() -- 根据package类型确定安装目录 print("Installing version: " .. pkg_version) local install_dir if meta.type and meta.type == "api" then install_dir = "/leonos/apis" else install_dir = "/app" -- 默认安装到app目录 end -- 确保安装目录存在 if not fs.exists(install_dir) then fs.makeDir(install_dir) end local success_count = 0 local fail_count = 0 for _, file_path in ipairs(meta.files or {}) do local dest = fs.combine(install_dir, file_path) if download_github_package_file(repo_path, file_path, dest) then print("Installed: " .. file_path) success_count = success_count + 1 else fail_count = fail_count + 1 end end if fail_count > 0 and success_count == 0 then print("Error: Failed to install any files.") return false end -- 记录安装信息 installed_db.packages[pkg_name] = { version = pkg_version, installed = os.time(), description = meta.description or "", author = meta.author or "", source = "github", repo_path = repo_path } if save_installed_db(installed_db) then print("Package installed successfully.") if fail_count > 0 then print("Note: " .. fail_count .. " files failed to install.") end return true else print("Failed to update package database.") return false end end -- 安装包 local function install_package(pkg_name, options) print("Installing package: " .. pkg_name) ensure_dirs() local installed_db = load_installed_db() or { packages = {} } -- 检查是否已安装 if installed_db.packages[pkg_name] and not options.force then print("Package already installed. Use --force to reinstall.") return false end -- 检查是否为GitHub包格式 (user/repo) if pkg_name:find("/") then return install_github_package(pkg_name, options) end -- 本地包安装逻辑 local pkg_path = fs.combine(pkg_config.local_pkg_dir, pkg_name) if not fs.exists(pkg_path) then print("Package not found in local repository.") return false end -- 查找最新版本 local versions = fs.list(pkg_path) if #versions == 0 then print("No versions found for package.") return false end table.sort(versions) local latest_version = versions[#versions] local version_path = fs.combine(pkg_path, latest_version) -- 读取包元数据 local meta_path = fs.combine(version_path, "package.json") if not fs.exists(meta_path) then print("Package metadata not found.") return false end local file = io.open(meta_path, "r") local meta_content = file:read("*a") file:close() local ok, meta = pcall(textutils.unserializeJSON, meta_content) if not ok or not meta then print("Invalid package metadata.") return false end -- 根据package类型确定安装目录 print("Installing version: " .. latest_version) local install_dir if meta.type and meta.type == "api" then install_dir = "/leonos/apis" else install_dir = "/app" -- 默认安装到app目录 end -- 确保安装目录存在 if not fs.exists(install_dir) then fs.makeDir(install_dir) end for _, file_path in ipairs(meta.files or {}) do local src = fs.combine(version_path, file_path) local dest = fs.combine(install_dir, file_path) -- 确保目标目录存在 local dest_dir = fs.getDir(dest) if not fs.exists(dest_dir) then fs.makeDir(dest_dir) end -- 复制文件 if fs.exists(src) then fs.copy(src, dest) print("Installed: " .. file_path) else print("Warning: File not found: " .. src) end end -- 记录安装信息 installed_db.packages[pkg_name] = { version = latest_version, installed = os.time(), description = meta.description or "", author = meta.author or "" } if save_installed_db(installed_db) then print("Package installed successfully.") return true else print("Failed to update package database.") return false end end -- 更新包 local function update_package(pkg_name, options) print("Updating package: " .. (pkg_name or "all packages")) local installed_db = load_installed_db() if pkg_name then -- 更新单个包 if not installed_db.packages[pkg_name] then print("Package not installed.") return false end -- 检查本地仓库是否有新版本 local pkg_path = fs.combine(pkg_config.local_pkg_dir, pkg_name) if not fs.exists(pkg_path) then print("Package not found in local repository.") return false end local versions = fs.list(pkg_path) if #versions == 0 then print("No versions found for package.") return false end table.sort(versions) local latest_version = versions[#versions] local current_version = installed_db.packages[pkg_name].version if latest_version == current_version and not options.force then print("Package is already up to date.") return true end -- 移除旧版本 if not remove_package(pkg_name) then print("Failed to remove old version.") return false end -- 安装新版本 return install_package(pkg_name, { force = true }) else -- 更新所有包 local count = 0 for name, _ in pairs(installed_db.packages) do print("Updating " .. name .. "...") if update_package(name, options) then count = count + 1 end end print("Updated " .. count .. " packages.") end return true end -- 移除包 local function remove_package(pkg_name) print("Removing package: " .. pkg_name) local installed_db = load_installed_db() if not installed_db.packages[pkg_name] then print("Package not installed.") return false end -- 读取包元数据以了解要删除的文件 local pkg_info = installed_db.packages[pkg_name] local pkg_path = fs.combine(pkg_config.local_pkg_dir, pkg_name) local version_path = fs.combine(pkg_path, pkg_info.version) local meta_path = fs.combine(version_path, "package.json") if fs.exists(meta_path) then local file = io.open(meta_path, "r") local meta_content = file:read("*a") file:close() local ok, meta = pcall(textutils.unserializeJSON, meta_content) if ok and meta then -- 移除包文件 for _, file_path in ipairs(meta.files or {}) do local dest if meta.type and meta.type == "api" then dest = fs.combine("/leonos/apis", file_path) else dest = fs.combine("/app", file_path) end if fs.exists(dest) then fs.delete(dest) print("Removed: " .. file_path) end end end end -- 从数据库中删除 installed_db.packages[pkg_name] = nil if save_installed_db(installed_db) then print("Package removed successfully.") return true else print("Failed to update package database.") return false end end -- 列出已安装的包 local function list_packages() local installed_db = load_installed_db() if not installed_db.packages or not next(installed_db.packages) then print("No packages installed.") return end print("Installed packages:") print("====================") for name, info in pairs(installed_db.packages) do print(name .. " (v" .. info.version .. ")") print(" Description: " .. (info.description or "No description")) print(" Author: " .. (info.author or "Unknown")) print(" Installed: " .. os.date("%Y-%m-%d %H:%M:%S", info.installed)) print("----------------") end end -- 搜索包 local function search_packages(query) print("Searching for packages matching: " .. query) -- 这里是搜索逻辑的占位符 -- 实际实现需要查询包仓库 -- 模拟搜索结果 print("Found 2 packages:") print(" example-pkg - An example package") print(" demo-app - A demonstration application") end -- 显示包信息 local function show_package_info(pkg_name) print("Information for package: " .. pkg_name) -- 这里是获取包信息逻辑的占位符 -- 模拟包信息 print(" Name: " .. pkg_name) print(" Version: 1.0.0") print(" Description: A sample package for LeonOS") print(" Author: LeonOS Team") print(" Dependencies: none") end -- 主函数 local function main(args) if #args == 0 then show_help() return end local command = args[1] local options = { force = false, local_file = false } -- 解析选项 local pkg_args = {} for i=2, #args do if args[i] == "--force" then options.force = true elseif args[i] == "--local" then options.local_file = true else table.insert(pkg_args, args[i]) end end -- 处理命令 if command == "install" then if #pkg_args < 1 then print("Error: Missing package name.") show_help() else install_package(pkg_args[1], options) end elseif command == "update" then update_package(pkg_args[1], options) elseif command == "remove" or command == "uninstall" then if #pkg_args < 1 then print("Error: Missing package name.") show_help() else remove_package(pkg_args[1]) end elseif command == "list" then list_packages() elseif command == "search" then if #pkg_args < 1 then print("Error: Missing search query.") show_help() else search_packages(pkg_args[1]) end elseif command == "info" then if #pkg_args < 1 then print("Error: Missing package name.") show_help() else show_package_info(pkg_args[1]) end elseif command == "init" then if #pkg_args < 1 then print("Error: Missing package name.") show_help() else create_package(pkg_args[1]) end elseif command == "help" then show_help() else print("Error: Unknown command '" .. command .. "'") show_help() end end -- 运行主函数 local args = {...} -- 过滤掉可能的空参数 local filtered_args = {} for _, arg in ipairs(args) do if arg and arg ~= "" then table.insert(filtered_args, arg) end end main(filtered_args)