v1.0.0 - Added Argentum as well as other major improvements and bugfixes.

This commit is contained in:
TheWahlolly
2025-05-18 19:16:30 +03:00
parent 521d52a26e
commit 95c235e65b
19 changed files with 951 additions and 42 deletions
+550
View File
@@ -0,0 +1,550 @@
local packages = {...}
local command = packages[1]
table.remove(packages, 1)
local fs = import("filesystem")
local agReg = import("/argentum/registry.cfg")
if not command then
shell.run("help argentum")
return
end
if not component.list("internet")() then
print("\27[91mThis program requires an internet card to run.")
return
end
local internet = component.proxy(component.list("internet")())
local source
if table.find(packages, "-s") then
source = table.remove(packages, table.find(packages, "-s") + 1)
table.remove(packages, table.find(packages, "-s"))
print("Using " .. source .. " as package source")
elseif table.find(packages, "--source") then
source = table.remove(packages, table.find(packages, "--source") + 1)
table.remove(packages, table.find(packages, "--source"))
print("Using " .. source .. " as package source")
else
print("Using main registry as package source")
end
if source and source:sub(1, 1) == "/" and source:sub(-1, -1) ~= "/" then
source = source .. "/"
end
local packageList = table.copy(packages)
local function getFile(path)
if path:sub(1,1) == "/" then
if not fs.exists(path) then
return false, "file does not exist"
end
local handle, data, tmpdata = fs.open(path, "r"), "", nil
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
return data
else
local request, data, tmpdata = nil, "", nil
local status, errorMessage = pcall(function()
request = internet.request(path)
request:finishConnect()
end)
if not status then
return false, errorMessage
end
local responseCode = request:response()
if responseCode and responseCode ~= 200 then
return false, responseCode
end
repeat
tmpdata = request.read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
return data
end
end
local i = 1
local function getAgConfig(package, source)
source = source or agReg[package]
local data, errorMessage = getFile(source .. "argentum.cfg")
if not data or data == "" then
print("\27[91mCould not fetch Ag config: " .. (errorMessage or "returned nil data"))
return false
end
local func, errorMessage = load(data, "=argentum.cfg", "bt", {})
if not func then
print("\27[91mCould not fetch Ag config: " .. errorMessage .. "\nPlease contact the package owner.")
return false
end
local agcfg
local status, errorMessage = pcall(function()
agcfg = func()
end)
if not status then
print("\27[91mCould not fetch Ag config: " .. errorMessage .. "\nPlease contact the package owner.")
return false
end
if not agcfg[package] or not agcfg[package].maindir or not agcfg[package].directories or not agcfg[package].files or not agcfg[package].version then
local response = ("\27[91mAg config of " .. package .. " is improperly configured.\nPlease contact the package owner.")
end
return agcfg
end
local function doChecks(package)
if not agReg[package] and not source then
print("\27[91mPackage " .. package .. " does not exist.")
return false
end
if fs.exists("/argentum/store/" .. package) then
print("\27[91mPackage " .. package .. " is already installed.")
return false
end
agcfg = getAgConfig(package, source)
if not agcfg then
return false
end
if agcfg[package].dependencies then
for _, dependency in ipairs(agcfg[package].dependencies) do
if not agReg[dependency] and not agcfg[dependency] then
local response = read(nil, "\27[91mPackage " .. package .. " requires dependency " .. dependency .. " that does not exist.\n[A - Abort/s - Skip]")
if response:lower() ~= "s" then
fs.remove("/argentum/store/" .. package)
return false
end
end
end
for _, dependency in pairs(agcfg[package].dependencies) do
print(package .. " depends on " .. dependency)
if not table.find(packages, dependency) and doChecks(dependency) then
table.insert(packages, table.find(packages, package), dependency)
table.insert(packageList, dependency)
i = i + 1
end
end
end
return true
end
local function installPackage(package)
print("Installing " .. package .. "...")
local agcfg = getAgConfig(package, source)
if not agcfg then
return false
end
local source = source or agReg[package]
local packageStore = "V" .. agcfg[package].version
if agcfg[package].dependencies then
for _, dependency in ipairs(agcfg[package].dependencies) do
if not agReg[dependency] and not agcfg[dependency] then
local response = read(nil, "\27[91mPackage " .. package .. " requires dependency " .. dependency .. " that does not exist.\n[A - Abort/s - Skip]")
if response:lower() ~= "s" then
fs.remove("/argentum/store/" .. package)
return false
end
end
end
for _, dependency in pairs(agcfg[package].dependencies) do
if agReg[dependency] or agcfg[dependency] then
--installPackage(dependency)
packageStore = packageStore .. "\nD" .. dependency
end
end
end
if agcfg[package].directories then
for _, directory in pairs(agcfg[package].directories) do
if directory:sub(-1, -1) ~= "/" then
directory = directory .. "/"
end
packageStore = "A" .. directory .. "\n" .. packageStore
if not fs.exists(directory) then
fs.makeDirectory(directory)
end
end
end
for _, file in ipairs(agcfg[package].files) do
::retry::
print(" Downloading " .. file .. "...")
local data, errorMessage = getFile(source .. agcfg[package].maindir .. file)
if not data then
local response = read(nil, "\27[91mCould not fetch " .. file .. ": " .. errorMessage .. "\n\27[0m[a - Abort/R - Retry/s - Skip]")
if response:lower() == "a" then
fs.remove("/argentum/store/" .. package)
return false
elseif response:lower() == "s" then
goto skip
else
goto retry
end
end
if fs.exists(file) then
if not fs.exists("/argentum/store/" .. package .. "/files/" .. file:match("(.*/)")) then
fs.makeDirectory("/argentum/store/" .. package .. "/files/" .. file:match("(.*/)"))
end
fs.copy(file, "/argentum/store/" .. package .. "/files/" .. file)
packageStore = packageStore .. "\nM" .. file
else
packageStore = packageStore .. "\nA" .. file
end
local handle = fs.open(file, "w")
handle:write(data)
handle:close()
::skip::
end
fs.makeDirectory("/argentum/store/" .. package)
local handle = fs.open("/argentum/store/" .. package .. "/package.cfg", "w")
handle:write(packageStore)
handle:close()
return true
end
local function removePackage(package)
print("Removing " .. package .. "...")
if not fs.exists("/argentum/store/" .. package .. "/package.cfg") then
print("\27[91mLocal Ag config of " .. package .. " does not exist.")
return false
end
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/package.cfg", "r"), "", nil
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
for line in (data.."\n"):gmatch("(.-)\n") do
if line:sub(1, 1) == "A" then
::retry::
print(" Removing " .. line:sub(2) .. "...")
if line:sub(-1, -1) == "/" and fs.list(line:sub(2))[1] then
print(" There are still files in " .. line:sub(2) .. ". Skipping.")
else
local result, errorMessage = fs.remove(line:sub(2))
if not result then
local response = read(nil, "\27[91mFailed to remove " .. line:sub(2) .. ": " .. errorMessage .. "\n\27[0m[a - Abort/r - Retry/S - Skip]")
if response:lower() == "a" then
return false
elseif response:lower() == "r" then
goto retry
end
end
end
elseif line:sub(1, 1) == "M" then
::retry::
print(" Reverting " .. line:sub(2) .. "...")
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/files/" .. line:sub(2), "r"), "", nil
if not handle then
local response = read(nil, "\27[91mFailed to revert " .. line:sub(2) .. ": " .. data .. "\n\27[0m[a - Abort/R - Retry/s - Skip]") -- this is pretty stupid but i think the error message would get pushed to data
if response:lower() == "a" then
return false
elseif response:lower() == "s" then
goto skip
else
goto retry
end
end
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
local handle = fs.open(line:sub(2), "w")
handle:write(data)
handle:close()
::skip::
end
end
fs.remove("/argentum/store/" .. package .. "/")
return true
end
-- Update registry
local fails = {}
if command == "install" then
if not packages or not packages[1] then
print("Please specify packages to install.")
return
end
while true do
if not doChecks(packages[i]) then
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
end
i = i + 1
if i > #packages then
break
end
end
local answer
if #fails == 0 then
print("Packages that will be installed: " .. table.concat(packageList, ", "))
if read(nil, "Would you like to proceed? [Y/n] "):lower() == "n" then
return
end
elseif #packageList == 0 then
print("None of the packages can be installed.")
return
else
print("Some packages cannot be installed.")
print("Packages that will be installed: " .. table.concat(packageList, ", "))
print("Packages that cannot be installed: " .. table.concat(fails, ", "))
if read(nil, "Would you like to proceed? [y/N] "):lower() ~= "y" then
return
end
end
for _, failedPackage in pairs(fails) do
table.remove(packages, table.find(packages, failedPackage))
end
fails = {}
for _, package in ipairs(packages) do
if not installPackage(package) then
table.insert(fails, package)
table.remove(packageList, table.find(packageList, package))
end
end
if #fails == 0 then
print("Installation completed successfully.")
print("Packages installed: " .. table.concat(packageList, ", "))
elseif #packageList == 0 then
print("All packages failed to install.")
print("Packages that could not be installed: " .. table.concat(fails, ", "))
else
print("Some packages failed to install.")
print("Packages installed: " .. table.concat(packageList, ", "))
print("Packages that could not be installed: " .. table.concat(fails, ", "))
end
elseif command == "remove" then
if not packages or not packages[1] then
print("Please specify packages to remove.")
return
end
while true do
if not fs.exists("/argentum/store/" .. packages[i]) then
print("\27[91mPackage " .. packages[i] .. " is not installed.")
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
table.remove(packages, table.find(packages, packages[i]))
i = i - 1
elseif packages[i] == "halyde" then -- yes, this stuff is hard-coded.
print("\27[91mFor obvious reasons, you can't uninstall Halyde.")
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
table.remove(packages, table.find(packages, packages[i]))
i = i - 1
elseif packages[i] == "argentum" then
print("\27[91mFor obvious reasons, you can't uninstall Argentum.")
print("\27[91mFor obvious reasons, you can't uninstall Halyde.")
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
table.remove(packages, table.find(packages, packages[i]))
i = i - 1
end
i = i + 1
if i > #packages then
break
end
end
-- do dependency checks
local packagesInstalled = fs.list("/argentum/store")
for _, currentPackage in pairs(packagesInstalled) do
if currentPackage:sub(-1, -1) == "/" and fs.exists("/argentum/store/" .. currentPackage .. "package.cfg") then
local handle, data, tmpdata = fs.open("/argentum/store/" .. currentPackage .. "package.cfg", "r"), "", nil
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
for line in (data.."\n"):gmatch("(.-)\n") do
for i = 1, #packages do
if line == "D" .. packages[i] then
print(packages[i] .. " depends on " .. currentPackage:sub(1, -2))
if not table.find(packages, currentPackage:sub(1, -2)) then
table.insert(packages, table.find(packages, packages[i]), currentPackage:sub(1, -2))
table.insert(packageList, currentPackage:sub(1, -2))
i = i + 1
end
end
end
end
end
end
local answer
if #fails == 0 then
print("Packages that will be removed: " .. table.concat(packageList, ", "))
if read(nil, "Would you like to proceed? [Y/n] "):lower() == "n" then
return
end
elseif #packageList == 0 then
print("None of the packages can be removed.")
return
else
print("Some packages cannot be removed.")
print("Packages that will be removed: " .. table.concat(packageList, ", "))
print("Packages that cannot be removed: " .. table.concat(fails, ", "))
if read(nil, "Would you like to proceed? [y/N] "):lower() ~= "y" then
return
end
end
for _, failedPackage in pairs(fails) do
table.remove(packages, table.find(packages, failedPackage))
end
fails = {}
for _, package in ipairs(packages) do
if not removePackage(package) then
table.insert(fails, package)
table.remove(packageList, table.find(packageList, package))
end
end
if #fails == 0 then
print("Removal completed successfully.")
print("Packages removed: " .. table.concat(packageList, ", "))
elseif #packageList == 0 then
print("All packages failed to be removed.")
print("Packages that could not be removed: " .. table.concat(fails, ", "))
else
print("Some packages failed to be removed.")
print("Packages removed: " .. table.concat(packageList, ", "))
print("Packages that could not be removed: " .. table.concat(fails, ", "))
end
elseif command == "update" then
if not packages[1] then
local packagesInstalled = fs.list("/argentum/store/")
for _, currentPackage in pairs(packagesInstalled) do
if currentPackage:sub(-1, -1) == "/" and fs.exists("/argentum/store/" .. currentPackage .. "package.cfg") then
table.insert(packages, currentPackage:sub(1, -2))
table.insert(packageList, currentPackage:sub(1, -2))
end
end
end
while true do
if not fs.exists("/argentum/store/" .. packages[i]) then
print("\27[91mPackage " .. packages[i] .. " is not installed.")
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
table.remove(packages, table.find(packages, packages[i]))
i = i - 1
end
i = i + 1
if i > #packages then
break
end
end
local answer
if #fails == 0 then
print("Packages that will be updated: " .. table.concat(packageList, ", "))
if read(nil, "Would you like to proceed? [Y/n] "):lower() == "n" then
return
end
elseif #packageList == 0 then
print("None of the packages can be updated.")
return
else
print("Some packages cannot be updated.")
print("Packages that will be updated: " .. table.concat(packageList, ", "))
print("Packages that cannot be updated: " .. table.concat(fails, ", "))
if read(nil, "Would you like to proceed? [y/N] "):lower() ~= "y" then
return
end
end
for _, failedPackage in pairs(fails) do
table.remove(packages, table.find(packages, failedPackage))
end
fails = {}
for _, package in pairs(packages) do
local agcfg = getAgConfig(package, source)
if not agcfg then
return false
end
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/package.cfg", "r"), "", nil
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
local version = "0.0.0"
for line in (data.."\n"):gmatch("(.-)\n") do
if line:sub(1, 1) == "V" then
version = line:sub(2)
break
end
end
local handle, data, tmpdata = fs.open("/argentum/store/" .. package .. "/package.cfg", "r"), "", nil
repeat
tmpdata = handle:read(math.huge)
data = data .. (tmpdata or "")
until not tmpdata
handle:close()
local version = "0.0.0"
for line in (data.."\n"):gmatch("(.-)\n") do
if line:sub(1, 1) == "V" then
version = line:sub(2)
break
end
end
if agcfg[package].version == version then
print(package .. " is up to date")
goto skip
end
if not removePackage(package) then
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
table.remove(packages, table.find(packages, packages[i]))
goto skip
end
if not installPackage(package) then
table.insert(fails, packages[i])
table.remove(packageList, table.find(packageList, packages[i]))
table.remove(packages, table.find(packages, packages[i]))
goto skip
end
::skip::
end
if #fails == 0 then
print("Update completed successfully.")
print("Packages updated: " .. table.concat(packageList, ", "))
elseif #packageList == 0 then
print("All packages failed to update.")
print("Packages that could not update: " .. table.concat(fails, ", "))
else
print("Some packages failed to update.")
print("Packages updated: " .. table.concat(packageList, ", "))
print("Packages that could not update: " .. table.concat(fails, ", "))
end
elseif command == "info" then
if not packages[1] then
print("Please specify a package to show information about.")
return
end
if not agReg[packages[1]] and not source then
print("\27[91mPackage " .. packages[1] .. " does not exist.")
return
end
local agcfg = getAgConfig(packages[1], source)
if not agcfg then
return false
end
print("\27[93m" .. packages[1] .. "\27[0m v" .. agcfg[packages[1]].version .. "\n " .. (agcfg[packages[1]].description or "No description."):gsub("\n", " \n"))
elseif command == "search" then
if not packages[1] then
print("Please specify a search term.")
return
end
local searchResults = {}
for packageName, _ in pairs(agReg) do
if packageName:find(packages[1], 1, true) then
table.insert(searchResults, packageName)
end
end
if not searchResults[1] then
print("No search results found for " .. packages[1] .. ".")
return
end
table.sort(searchResults)
print("Search results: \n " .. table.concat(searchResults, "\n "))
elseif command == "list" then
local sortedPackages = {}
for packageName, _ in pairs(agReg) do
table.insert(sortedPackages, packageName)
end
table.sort(sortedPackages)
print("List of available Ag packages: \n " .. table.concat(sortedPackages, "\n "))
else
shell.run("help ag")
end
+6 -2
View File
@@ -183,7 +183,7 @@ local function processEvent(args)
changesMade = true
cursorRenderFlag = true
cursorWhite = true
if cursorPosY + scrollPosY - 1 > 1 then
if cursorPosX == 1 and cursorPosY + scrollPosY - 1 > 1 then
cursorPosY = cursorPosY - 1
if cursorPosY < 1 then
scrollPosY = scrollPosY - 1
@@ -256,7 +256,11 @@ local function save()
end
local handle, errorMessage = fs.open(savepath, "w")
if handle then
handle:write(table.concat(tmpdata, "\n"))
if table.concat(tmpdata, "\n"):sub(-1, -1) == "\n" then
handle:write(table.concat(tmpdata, "\n"))
else
handle:write(table.concat(tmpdata, "\n") .. "\n") -- add a newline at the end to follow POSIX standards
end
handle:close()
rawset(1, height - 1, "\27[107m\27[30m" .. filestring .. string.rep(" ", width))
else
+1 -1
View File
@@ -14,7 +14,7 @@ print("\27[92mComponents\27[0m: "..tostring(componentCounter))
termlib.cursorPosX = 17
print("\27[92mCoroutines\27[0m: "..tostring(#cormgr.corList))
termlib.cursorPosX = 17
print("\27[92mBattery\27[0m: "..tostring(math.floor(computer.maxEnergy() / computer.energy() * 1000 + 0.5) / 10).."%")
print("\27[92mBattery\27[0m: "..tostring(math.floor(computer.energy() / computer.maxEnergy() * 1000 + 0.5) / 10).."%")
termlib.cursorPosX = 17
local totalMemory = computer.totalMemory()
local usedMemory = computer.totalMemory() - computer.freeMemory()
+18
View File
@@ -0,0 +1,18 @@
Usage: argentum [COMMAND] [PACKAGES]
Uses the Argentum package manager.
COMMAND Specifies the operation for Ag to do.
install Installs packages.
remove Removes packages.
update Updates packages.
list Lists all available packages.
search Searches all available packages.
info Shows information on a specific package.
PACKAGES* Packages to apply operations to.
Examples:
ag install hal-draw Installs the hal-draw package.
ag list Lists all packages.
ag info hal-draw Shows information about hal-draw.
ag update hal-draw Updates the hal-draw package if it's not at the newest version.
ag update Updates all packages.
+14 -13
View File
@@ -1,16 +1,17 @@
All current Halyde shell commands:
cat Concatenates and prints a file.
cd Changes directory.
clear Clears the screen.
cp Copies a file.
echo Prints a message.
fetch Displays system information.
help Shows this.
ls Lists files.
lua Starts the Lua shell.
mv Moves/renames a file.
rm Deletes a file.
edit Opens the text editor.
All default Halyde shell commands:
cat Concatenates and prints a file.
cd Changes directory.
clear Clears the screen.
cp Copies a file.
echo Prints a message.
fetch Displays system information.
help Shows this.
ls Lists files.
lua Starts the Lua shell.
mv Moves/renames a file.
rm Deletes a file.
edit Opens the text editor.
argentum Uses the Argentum package manager.
You can get additional information on any app or command by running:
help [COMMAND]
+7
View File
@@ -0,0 +1,7 @@
Usage: mkdir [PATH]
Removes files and directories.
PATH Specifies the path to create the directory in.
Examples:
mkdir a Creates a directory named a in the current shell working directory.
+3 -6
View File
@@ -1,10 +1,7 @@
Usage: rm [FLAGS] [PATH]
Usage: rm [PATH]
Removes files and directories.
-r, --recursive Removes directories and their contents recursively.
-f, --force Ignores nonexistent files or directories.
PATH Specifies the file to be moved/renamed.
PATH Specifies the file to be moved/renamed.
Examples:
rm a.txt Removes a.txt in the current shell working directory.
rm -r -f /halyde/core/ Removes everything in /halyde/core/ forcedly and recursively.
rm a.txt Removes a.txt in the current shell working directory.
+16
View File
@@ -0,0 +1,16 @@
local args = {...}
local directory = args[1]
args = nil
local fs = import("filesystem")
if not directory then
shell.run("help mkdir")
return
end
if directory:sub(1, 1) ~= "/" then
directory = shell.workingDirectory .. directory
end
if fs.exists(file) then
print("\27[91mAn object already exists at the specified path.")
end
fs.makeDirectory(file)