From fb33b53b42eb1eaf16eb9355edaa3f294ef186fa Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Mon, 1 Sep 2025 22:03:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=8C=85=E7=AE=A1=E7=90=86=E5=99=A8):=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0LeonOS=E5=8C=85=E7=AE=A1=E7=90=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=9F=BA=E7=A1=80=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加包管理器核心程序(pkg.lua)及相关支持文件 - 实现包安装、更新、移除、列表、搜索等功能 - 添加包元数据结构和本地存储管理 - 包含示例包和命令补全支持 - 更新系统版本至0.3.0 --- data/computercraft/lua/bios.lua | 6 +- .../computercraft/lua/rom/completions/pkg.lua | 31 ++ .../lua/rom/completions/tree.lua | 7 + .../example-pkg/1.0.0/completions/example.lua | 10 + .../packages/example-pkg/1.0.0/package.json | 12 + .../example-pkg/1.0.0/programs/example.lua | 28 ++ .../lua/rom/packages/installed.json | 3 + data/computercraft/lua/rom/programs/calc.lua | 2 +- data/computercraft/lua/rom/programs/pkg.lua | 384 ++++++++++++++++++ data/computercraft/lua/rom/programs/tree.lua | 93 +++++ installer.lua | 2 +- 11 files changed, 573 insertions(+), 5 deletions(-) create mode 100644 data/computercraft/lua/rom/completions/pkg.lua create mode 100644 data/computercraft/lua/rom/completions/tree.lua create mode 100644 data/computercraft/lua/rom/packages/example-pkg/1.0.0/completions/example.lua create mode 100644 data/computercraft/lua/rom/packages/example-pkg/1.0.0/package.json create mode 100644 data/computercraft/lua/rom/packages/example-pkg/1.0.0/programs/example.lua create mode 100644 data/computercraft/lua/rom/packages/installed.json create mode 100644 data/computercraft/lua/rom/programs/pkg.lua create mode 100644 data/computercraft/lua/rom/programs/tree.lua diff --git a/data/computercraft/lua/bios.lua b/data/computercraft/lua/bios.lua index 2ac8510..3cc742d 100644 --- a/data/computercraft/lua/bios.lua +++ b/data/computercraft/lua/bios.lua @@ -1,4 +1,4 @@ -_G._HOST = _G._HOST .. " (LeonOS 0.2.7)" +_G._HOST = _G._HOST .. " (LeonOS 0.3.0)" local fs = rawget(_G, "fs") _G._RC_ROM_DIR = _RC_ROM_DIR or (...) and fs.exists("/leonos") and "/leonos" or "/rom" @@ -31,8 +31,8 @@ local rc = { _NAME = "LeonOS", _VERSION = { major = 0, - minor = 2, - patch = 7 + minor = 3, + patch = 0 }, queueEvent = pull(os, "queueEvent"), startTimer = pull(os, "startTimer"), diff --git a/data/computercraft/lua/rom/completions/pkg.lua b/data/computercraft/lua/rom/completions/pkg.lua new file mode 100644 index 0000000..3a51db4 --- /dev/null +++ b/data/computercraft/lua/rom/completions/pkg.lua @@ -0,0 +1,31 @@ +-- pkg command completion +local shell = require("shell") +local completion = require("cc.shell.completion") + +-- 定义pkg命令的补全函数 +local function pkg_completion(shell, index, text, previous) + -- 子命令补全 + if index == 1 then + return completion.choice(text, {"install", "update", "remove", "list", "search", "info", "help"}) + end + + -- 选项补全 (--force, --local) + if text:sub(1, 2) == "--" then + return completion.choice(text, {"--force", "--local"}) + end + + -- 命令特定补全 + local command = previous[1] + if command == "install" or command == "update" or command == "remove" or command == "info" then + -- 这里可以添加包名补全逻辑 + -- 目前返回空列表 + return {} + elseif command == "search" then + -- 搜索查询不需要特定补全 + return {} + end + + return {} +end + +shell.setCompletionFunction("pkg", pkg_completion) \ No newline at end of file diff --git a/data/computercraft/lua/rom/completions/tree.lua b/data/computercraft/lua/rom/completions/tree.lua new file mode 100644 index 0000000..41b47ba --- /dev/null +++ b/data/computercraft/lua/rom/completions/tree.lua @@ -0,0 +1,7 @@ +-- tree command completion +local shell = require("shell") +local completion = require("cc.shell.completion") + +shell.setCompletionFunction("tree", completion.build( + {completion.dir, many = true} +)) \ No newline at end of file diff --git a/data/computercraft/lua/rom/packages/example-pkg/1.0.0/completions/example.lua b/data/computercraft/lua/rom/packages/example-pkg/1.0.0/completions/example.lua new file mode 100644 index 0000000..97024dc --- /dev/null +++ b/data/computercraft/lua/rom/packages/example-pkg/1.0.0/completions/example.lua @@ -0,0 +1,10 @@ +-- example command completion + +local shell = require('shell') +local completion = require('completion') + +-- 为example命令设置补全 +shell.setCompletionFunction('example', completion.build({ + -- 这里可以添加example命令的补全逻辑 + completion.choice{'--help', '-h'} +})) \ No newline at end of file diff --git a/data/computercraft/lua/rom/packages/example-pkg/1.0.0/package.json b/data/computercraft/lua/rom/packages/example-pkg/1.0.0/package.json new file mode 100644 index 0000000..ccfb38b --- /dev/null +++ b/data/computercraft/lua/rom/packages/example-pkg/1.0.0/package.json @@ -0,0 +1,12 @@ +{ + "name": "example-pkg", + "version": "1.0.0", + "description": "An example package for LeonOS", + "author": "LeonOS Team", + "license": "MIT", + "dependencies": {}, + "files": [ + "programs/example.lua", + "completions/example.lua" + ] +} \ No newline at end of file diff --git a/data/computercraft/lua/rom/packages/example-pkg/1.0.0/programs/example.lua b/data/computercraft/lua/rom/packages/example-pkg/1.0.0/programs/example.lua new file mode 100644 index 0000000..8256818 --- /dev/null +++ b/data/computercraft/lua/rom/packages/example-pkg/1.0.0/programs/example.lua @@ -0,0 +1,28 @@ +-- 示例包程序 +local colors = require('colors') + +function drawTopBar() + local w, h = term.getSize() + term.setBackgroundColor(colors.cyan) + term.setTextColor(colors.white) + term.setCursorPos(1, 1) + term.clearLine() + local title = "Example Package v1.0.0" + 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("\n这是一个示例包,展示了LeonOS包管理器的功能。") +print("\n使用方法:") +print(" pkg install example-pkg - 安装此包") +print(" pkg remove example-pkg - 卸载此包") +print(" pkg list - 列出已安装的包") +print("\n按任意键退出...") +os.pullEvent("key") +term.clear() +term.setCursorPos(1, 1) \ No newline at end of file diff --git a/data/computercraft/lua/rom/packages/installed.json b/data/computercraft/lua/rom/packages/installed.json new file mode 100644 index 0000000..9065101 --- /dev/null +++ b/data/computercraft/lua/rom/packages/installed.json @@ -0,0 +1,3 @@ +{ + "packages": {} +} \ No newline at end of file diff --git a/data/computercraft/lua/rom/programs/calc.lua b/data/computercraft/lua/rom/programs/calc.lua index a6d400b..dc0b9f5 100644 --- a/data/computercraft/lua/rom/programs/calc.lua +++ b/data/computercraft/lua/rom/programs/calc.lua @@ -19,7 +19,7 @@ local function drawTopBar() term.setCursorPos(1, 1) -- Draw top bar with centered title - local title = " LeonOS Calculator " + local title = "=== LeonOS Calculator ===" local padding = math.floor((w - #title) / 2) term.write(string.rep(" ", padding) .. title .. string.rep(" ", padding)) diff --git a/data/computercraft/lua/rom/programs/pkg.lua b/data/computercraft/lua/rom/programs/pkg.lua new file mode 100644 index 0000000..b8897bc --- /dev/null +++ b/data/computercraft/lua/rom/programs/pkg.lua @@ -0,0 +1,384 @@ +-- 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 = "/rom/packages", -- 本地包存储目录 + installed_db = "/rom/packages/installed.json", -- 已安装包数据库 + cache_dir = "/rom/cache" -- 缓存目录 +} + +-- 确保必要的目录存在 +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, { pretty = true })) + 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(" help Show this help message") + print("") + print("Options:") + print(" --local Install from local file") + print(" --force Force install/update") +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 + + -- 本地包安装逻辑 + 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 + + -- 安装文件 + print("Installing version: " .. latest_version) + for _, file_path in ipairs(meta.files or {}) do + local src = fs.combine(version_path, file_path) + local dest = fs.combine("/rom", 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 = fs.combine("/rom", file_path) + 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 == "help" then + show_help() + else + print("Error: Unknown command '" .. command .. "'") + show_help() + end +end + +-- 运行主函数 +main({...}) \ No newline at end of file diff --git a/data/computercraft/lua/rom/programs/tree.lua b/data/computercraft/lua/rom/programs/tree.lua new file mode 100644 index 0000000..00d2375 --- /dev/null +++ b/data/computercraft/lua/rom/programs/tree.lua @@ -0,0 +1,93 @@ +-- tree + +-- 程序顶部名称栏 +local term = require("term") +local colors = require("colors") + +-- 保存当前颜色设置 +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("=== Directory Tree Utility ===") + +-- 恢复颜色设置 +term.setTextColor(old_fg) +term.setBackgroundColor(old_bg) +term.at(1, 2) + +local args = {...} +local fs = require("fs") +local shell = require("shell") +local settings = require("settings") + +if #args == 0 then args[1] = shell.dir() end + +local show_hidden = settings.get("list.show_hidden") + +-- 递归打印目录树 +local function print_tree(dir, prefix, is_last) + if not fs.exists(dir) then + error(dir .. ": that directory does not exist", 0) + elseif not fs.isDir(dir) then + error(dir .. ": not a directory", 0) + end + + local raw_files = fs.list(dir) + local files, dirs = {}, {} + + -- 分离目录和文件 + for i=1, #raw_files, 1 do + local full = fs.combine(dir, raw_files[i]) + + if raw_files[i]:sub(1,1) ~= "." or show_hidden then + if fs.isDir(full) then + dirs[#dirs+1] = raw_files[i] + else + files[#files+1] = raw_files[i] + end + end + end + + -- 排序目录和文件 + table.sort(dirs) + table.sort(files) + + -- 打印当前目录 + if prefix == "" then + term.setTextColor(colors.yellow) + print(dir) + term.setTextColor(old_fg) + end + + -- 打印目录 + for i=1, #dirs, 1 do + local is_last_dir = (i == #dirs and #files == 0) + local connector = is_last_dir and "└── " or "├── " + local new_prefix = prefix .. (is_last_dir and " " or "│ ") + + term.setTextColor(colors.green) + print(prefix .. connector .. dirs[i]) + term.setTextColor(old_fg) + + -- 递归打印子目录 + print_tree(fs.combine(dir, dirs[i]), new_prefix, is_last_dir) + end + + -- 打印文件 + for i=1, #files, 1 do + local is_last_file = (i == #files) + local connector = is_last_file and "└── " or "├── " + + term.setTextColor(colors.white) + print(prefix .. connector .. files[i]) + term.setTextColor(old_fg) + end +end + +for i=1, #args, 1 do + print_tree(args[i], "", true) +end \ No newline at end of file diff --git a/installer.lua b/installer.lua index 10ce940..2f9428e 100644 --- a/installer.lua +++ b/installer.lua @@ -1,5 +1,5 @@ -- LeonOS installer -local INSTALLER_VERSION = "0.2.7" +local INSTALLER_VERSION = "0.3.0" local DEFAULT_ROM_DIR = "/leonos" print("Start loading LeonOS installer ("..INSTALLER_VERSION..")...")