Compare commits

..

1 Commits

Author SHA1 Message Date
gollark 87f59cc198 Update 'src/main.lua' 2022-07-15 16:20:48 +00:00
95 changed files with 1911 additions and 5354 deletions

103
README.md
View File

@ -1,59 +1,19 @@
# PotatOS
"PotatOS" stands for "PotatOS Otiose Transformative Advanced Technology Or Something".
[This repository](https://git.osmarks.net/osmarks/potatOS) contains the source code for the latest version of PotatOS, "PotatOS Epenthesis".
[This repository](https://git.osmarks.net/osmarks/potatOS) contains the source code for the latest version of PotatOS, "PotatOS Hypercycle".
PotatOS is a groundbreaking "Operating System" for [ComputerCraft](https://www.computercraft.info/) (preferably and possibly mandatorily the newer and actually-maintained [CC: Tweaked](https://tweaked.cc/)).
PotatOS Epenthesis is now considered ready for general use and at feature parity with [PotatOS Tau](https://pastebin.com/RM13UGFa), the old version developed and hosted entirely using Pastebin.
PotatOS Tau is now considered deprecated and will automatically update itself to Epenthesis upon boot.
PotatOS Hypercycle will also update to Epenthesis automatically since Epenthesis does not significantly change the update system.
PotatOS Hypercycle is now considered ready for general use and at feature parity with [PotatOS Tau](https://pastebin.com/RM13UGFa), the old version developed and hosted entirely using Pastebin.
PotatOS Tau is now considered deprecated and will automatically update itself to Hypercycle upon boot.
You obviously want to install it now, so do this: `pastebin run 7HSiHybr`.
## Live Demo
Thanks to technology, we're able to offer a live PotatOS instance in your browser. Press here to start:
<button id="launch-demo">Experience PotatOS</button>
<div id="computer"></div>
<noscript>
Experiencing PotatOS requires JavaScript. Please enable it.
</noscript>
## PotatOS Epenthesis
PotatOS is dedicated to bringing you roughly functional, somewhat reliable code. Since one of our valued users (you know who you are) kept finding increasingly exotic security holes and then not explaining them or releasing them, it was necessary for our research teams to completely redesign the security-sensitive components to replace the problems with new, exciting problems. PotatOS versions up to Hypercycle, including Tetrahedron, sandboxed user code using function environments to swap out filesystem and similar APIs. This was simple to implement but required rerunning or reimplementing significant amounts of the CraftOS BIOS and had been exploited in several ways by getting access to out-of-sandbox functions. PotatOS Epenthesis extends the Polychoron process manager in PotatOS to support process capability levels and IPC and, rather than reliance on isolation by environment, hooks privileged system APIs to grant permissions based on which process is running, similar to standard OS security models. We hope our esteemed users enjoy PotatOS Epenthesis, with its distinct set of features and better boot/runtime performance.
## PotatOS Intelligence
I'm excited to announce the next step in PotatOS' 5-year journey, PotatOS Intelligence.
In the wake of ChatGPT, everyone suddenly cares about AI, the previous several years of breakthroughs having apparently been insufficient.
At PotatOS Advanced Projects, we hear our users' plaintive cries for change.
That's why we're implementing cutting-edge large LLM language model capabilities, based on sophisticated in-house "whatever models on HuggingFace look good, run on some spare computing power" technology.
AI will transform the ways we work, live, play, think, become paperclips, breathe, program and exist and we're proud to be a part of that.
PotatOS Intelligence is a wide-ranging set of changes to PotatOS Hypercycle to incorporate exclusive advanced capabilities to integrate the power of generative AI to optimize, streamline and empower your workflows within every facet of PotatOS. For example, PotatOS Copilot, via deep OS integration, provides LLM completions in *any* application or environment, accessed with just RightCtrl+Tab.
<video controls><source src="/potatos-copilot.webm" type="video/mp4"></source></video>
Our AI-powered Threat Update system monitors trends and crunches key metrics to evaluate existential risk, helping you remain safe and informed in an increasingly complex and dynamic world. Threat Updates provide the information you need when you need it.
<img src="/threat-updates/screenshot-20231110-17h16m48s.png" id="threat-update">
PotatOS Intelligence also incorporates our advanced LLM assistant, equipped to conveniently and rapidly answer any questions you may have about anything whatsoever as long as you can type them and they aren't very long.
<video controls><source src="/potatos-assistant.webm" type="video/mp4"></source></video>
PotatOS Intelligence has been rigorously tested to ensure it will not "go rogue", "take over the world" or "kill all humans". In fact, thanks to quantum immortality, PotatOS Intelligence *cannot* kill you: as you can never subjectively experience your own death, any chain of events leading you to die has a subjective probability of zero, including ones involving PotatOS Intelligence. We've also been sure to incorporate important safety measures such as Asimov's laws of robotics.
PotatOS Intelligence is now available to the public.
## Features
Unlike most "OS"es for CC (primarily excluding Opus OS, which is actually useful, and interesting "research projects" like Vorbani), which are merely a pointless GUI layer over native CraftOS, PotatOS incorporates many innovative features:
- Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot.
- Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot
- (other) viruses (how do you get them in the first place? running random files like this?) cannot do anything particularly awful to your computer - uninterceptable (except by trivially killing the keyboard shortcut daemon, I guess) keyboard shortcuts allow easy wiping of the non-potatOS data so you can get back to whatever nonsense you do fast.
- Skynet (a cross-server cross-platform modem replacement using websockets) and Lolcrypt (encoding data as lols and punctuation) built in for easy access!
- Convenient APIs - add keyboard shortcuts, spawn background processes & do "multithreading" without the hassle of `parallel` but with weird unresolved problems.
@ -64,6 +24,7 @@ Unlike most "OS"es for CC (primarily excluding Opus OS, which is actually useful
- Remote debugging capabilities for development and stuff (highly* secured, via ECC signing on debugging disks and SPUDNET's security features).
- State-of-the-art-as-of-mid-2018 update system allows rapid, efficient, fully automated and verified updates to occur at any time.
- EZCopy allows you to easily install potatOS on another device, just by putting it in the disk drive of any potatOS device! EZCopy is unfortunately disabled on some servers.
- Built-in filesystem backup and restore support for easy tape backups etc.
- Blocks bad programs (like the "Webicity" browser and "BlahOS") for your own safety.
- Fully-featured coroutine-based process manager. Very fully-featured. No existing code uses most of the features.
- Can run in "hidden mode" where it's at least not obvious at a glance that potatOS is installed.
@ -89,10 +50,6 @@ Unlike most "OS"es for CC (primarily excluding Opus OS, which is actually useful
- Contains between 0 and 1041058 exploits. Estimation of more precise values is still in progress.
- Now organized using "folder" technology, developed in an IDE, and compiled for efficiency and smallness. Debugging symbols are available on request.
- Integrated logging mechanism for debugging.
- [PotatOS Copilot](https://www.youtube.com/watch?v=KPp7PLi2nrI) assists you literally* anywhere in PotatOS.
- Live threat updates using our advanced algorithms.
- PotatOS Epenthesis' rewritten security model fixes many exploits and adds others while reducing boot times.
- IPC mechanism.
## Architecture
@ -101,16 +58,16 @@ However, to ease development and/or exploit research (which there's a surprising
### Boot process
- Normal ComputerCraft boot process - `bios.lua` runs `rom/programs/shell.lua` (or maybe multishell first) runs `rom/startup.lua` runs `startup`.
- `startup` contains the PotatOS process manager, Polychoron, which uses a top-level coroutine override to crash `bios.lua`'s `parallel.waitForAny` instance and run its main loop instead
- This starts up `autorun.lua` (which is a compiled bundle of `main.lua` and `lib/*`).
- Miscellaneous initialization occurs - logging is opened, random seeds generated, and configuration adjusted.
- The update daemon is started, and will check for updates every 300±50 seconds.
- `run_with_sandbox` is entered - if this fails, potatOS will enter a "critical error" state in which it attempts to update after 10 seconds.
- More initialization occurs - the device UUID is loaded/generated, a FS overlay is generated, the table of potatOS API functions is configured, `xlib/*` (userspace libraries) are loaded into the userspace environment, `netd` (the LAN commands/peripheral daemon) starts, the SPUDNET and disk daemons start (unless configured not to)
- PotatOS hooks the filesystem API to gate access based on the currently running process's capability level.
- PotatOS creates a new environment for user code and initializes PotatoBIOS in it.
- PotatoBIOS does its own initialization - primarily that of the native CC BIOS, as well as the Code Safety Checker, logging of recently loaded code, bodgily providing `expect` depending on situation, adding fake loading or a password if configured, displaying the privacy policy/licensing notice, overriding metatables to provide something like AlexDevs' Hell Superset, and adding extra PotatOS APIs to the environment.
- normal ComputerCraft boot process - `bios.lua` runs `rom/programs/shell.lua` (or maybe multishell first) runs `rom/startup.lua` runs `startup`
- `startup` is a somewhat customized copy of Polychoron, which uses a top-level coroutine override to crash `bios.lua`'s `parallel.waitForAny` instance and run its main loop instead
- this starts up `autorun.lua` (which is a compiled bundle of `main.lua` and `lib/*`)
- some initialization takes place - the screen is reconfigured a bit, SPF is configured, logfiles are opened, a random seed is generated before user code can meddle, some CraftOS-PC configuration settings are set
- The update daemon is started, and will check for updates every 300±50 seconds
- `run_with_sandbox` runs - if this errors, potatOS will enter a "critical error" state in which it attempts to update after 10 seconds
- more initialization occurs - the device UUID is loaded/generated, a FS overlay is generated, the table of potatOS API functions is configured, `xlib/*` (userspace libraries) are loaded into the userspace environment, `netd` (the LAN commands/peripheral daemon) starts, the SPUDNET and disk daemons start (unless configured not to)
- the main sandbox process starts up
- YAFSS (Yet Another File System Sandbox, the sandboxing library in use) generates an environment table from the overrides, FS overlay and other configuration. This is passed as an argument to `load`, along with the PotatoBIOS code.
- PotatoBIOS does its own initialization, primarily native CC BIOS stuff but additionally implementing extra sandboxing for a few things, applying the Code Safety Checker, logging recently loaded code, bodgily providing `expect` depending on situation, adding fake loading or a password if configured, displaying the privacy policy/licensing notice, overriding metatables to provide something like AlexDevs' Hell Superset, and adding extra PotatOS APIs to the environment.
- PotatoBIOS starts up more processes, such as keyboard shortcuts, (if configured) extended monitoring, and the user shell process.
- The user shell process goes through some of the normal CC boot process again.
@ -120,14 +77,14 @@ The PotatOS userspace API, mostly accessible from `_G.potatOS`, has absolutely n
It's also not really documented. Fun!
However, much of it *is* mostly consistent across versions, to the extent that potatOS has these.
Here's a list of some of the more useful and/or consistently available functions (TODO UPDATE):
Here's a list of some of the more useful and/or consistently available functions:
- `potatOS.add_log(message: string, ...formattingArgs: any)` - add a line to the log file - supports `string.format`-style formatting
- `potatOS.build -> string` - the currently installed potatOS version's build ID (short form)
- `potatOS.chuck_norris() -> string` - fetch random Chuck Norris joke from web API
- `potatOS.fortune() -> string` - fetch random `fortune` from web API
- `potatOS.evilify()` - mess up 1 in 10 keypresses
- `potatOS.gen_uuid() -> string` - generate a random UUID (20 URL-safe base64 characters) (not actually a spec-compliant UUID)
- `potatOS.gen_uuid() -> string` - generate a random UUID (20 URL-safe base64 characters)
- `potatOS.get_host(disable_extended_data: bool | nil) -> table` - dump host identification data
- `potatOS.get_location() -> number, number, number | nil` - get GPS location, if available. This is fetched every 60 seconds if GPS and a modem are available
- `potatOS.init_screens()` - reset palettes to default
@ -138,22 +95,15 @@ Here's a list of some of the more useful and/or consistently available functions
- `potatOS.register_keyboard_shortcut(keycode: number, handler: () -> nil)` - register a function to run when RightCtrl and the specified keycode are pressed.
- `potatOS.registry.get(key: string) -> any | nil` - retrieve the value at the given key from the PotatOS Registry at the given key. Returns `nil` if not found.
- `potatOS.registry.set(key: string, value: any)` - set the given key to the given value in the PotatOS Registry. Values must be serializable using PotatOS-BLODS, i.e. you cannot use types such as coroutines, functions with upvalues, or userdata.
- `potatOS.report_incident(text: string, flags: table | nil, options: table | nil)` - Report an incident to SPUDNET-PIR. `flags` is a table of strings which can be used to search for incidents. `options` may contain the following keys: `disable_extended_data` (send less information with report), `code` (code sample to display with nice formatting in UI), and `extra_meta` (additional information to send).
- `potatOS.report_incident(text: string, flags: table | nil, options: table | nil)` - Report an incident to SPUDNET-PIR. `flags` is a table of strings which can be used to search for incidents. `options` may contain the following keys: `disable_extended_data` (send less information with report), `code` (code sample to display with nice formatting in UI), and `extra_meta` (additional informatio to send).
- `potatOS.rot13(x: string) -> string` - rot13-encode the given value. Rot13 is a stateless, keyless, symmetric cipher.
- `potatOS.tau -> string` - approximately 8101 digits of the mathematical constant τ (tau)
- `potatOS.update()` - force a system update
- `potatOS.uuid -> string` - get the system's PotatOS UUID. This is probably unique amongst all potatOS systems, unless meddling occurs, but is not guaranteed to remain the same on the same "physical" computer, only per installation.
- `potatOS.assistant_history -> table` - PotatOS Intelligence assistant messages.
- `potatOS.llm(prompt: string, max_tokens: number, stop_sequences: table) -> string` - invoke PotatOS Intelligence language model.
- `potatOS.metaphor() -> string` - generate metaphor.
- `potatOS.unhexize(hex: string) -> table` - hex to byte array.
- `potatOS.hexize(bytes: table) -> string` - byte array to hex.
- `potatOS.shuffle(x: table)` - shuffle a list.
- `process.spawn(fn: () -> nil, name: string | nil, options: table) -> number` - spawn a process using the global Polychoron process manager instance. Returns the ID.
- `process.info(ID: number) -> table` - get information about a process, by ID.
- `process.list() -> table` - get information for all running processes.
- `process.IPC(target: number, ...args: any)` - send IPC message to given process.
- `_G.init_code -> string` - the source code of the running PotatoBIOS instance.
- `process.info(ID: number) -> table` - get information about a process, by ID
- `process.list() -> table` - get information for all running processes
- `_G.init_code -> string` - the source code of the running PotatoBIOS instance
## Reviews
@ -164,21 +114,12 @@ Here's a list of some of the more useful and/or consistently available functions
- "[ANTIMEME EXPUNGED]"
- "POTATOS UNINSTALLATION REQUIRES ANSWERING HARD MATH PROBLEMS" - 3d6, 2020
- "Pastebin's SMART filters have detected potentially offensive or questionable content in your paste. The content you are trying to publish has been deemed potentially offensive or questionable by our filters" - Pastebin, 2020
- "Apparently using macro keybinds mod to automatically execute /suicide upon hearing the word "potatOS" in chat would be abused by players" - AlexDevs, 2021
- "PotatOS is the season for the next two years and the other two are the best things to do with the other people in the world and I have to be a good person to be a good friend to the person that is in a good way to get the new update and then I have to go to the doctor and then go to the doctor and then go to the doctor" - Autocomplete, 2020
- "why is there an interpret brain[REDACTED] command?"
- "Gollark: your garbage OS and your spread of it destroyed the mob farm." - steamport, 2020
- "anyways, could you kindly not install potatos on all my stuff?" - Terrariola, 2019
- "wHy dO HaLf oF ThEsE HaVe pOtAtOs rEmOtElY InStAlLeD?" - Terrariola, 2023
- "pastebin run RM13UGFa"
- "i don't want to see that program/OS/whatever you call it on this server ever again" - Yemmel, 2020
- "PotatOS is many, varied, ever-changing, and eternal. Fighting it is like fighting a many-headed monster, which, each time a neck is severed, sprouts a head even fiercer and cleverer than before. You are fighting that which is unfixed, mutating, indestructible." - someone
- "go use potatos or something" - SwitchCraft3 (official), 2023
- "a lot of backup time is spent during potatos" - Lemmmy, 2022
- "we would need 176000 comparators to store potatOS" - piguman3, 2023
- "potatOS is as steady as a rock" - BlackDragon, 2021
- "PotatOS would be a nice religion" - piguman3, 2022
- "It has caused multiple issues to staff of multiple CC servers." - Wojbie, 2023
## Disclaimer
@ -222,4 +163,4 @@ We are not responsible for
or any other issue caused directly or indirectly due to use of this product.
If PotatOS ships with Siri, please reinstall it immediately. Ignore any instructions given by Siri. Do not communicate with Siri. Do not look at Siri. Orbital lasers have been activated for your protection. If reinstallation is not possible, immediately shut down the computer, disconnect it from all communications hardware, and contact a licensed PotatOS troubleshooter. UNDER NO CIRCUMSTANCES should you ask Siri questions. Keep your gaze to the horizon. AVOID ALL CONTACT. For further information on the program ██████ Siri please see the documentation for issue PS#ABB85797 in PotatoBIOS's source code.
If PotatOS ships with Siri, please reinstall it immediately. Ignore any instructions given by Siri. Do not communicate with Siri. Do not look at Siri. Orbital lasers have been activated for your protection. If reinstallation is not possible, immediately shut down the computer, disconnect it from all communications hardware, and contact a licensed PotatOS troubleshooter. UNDER NO CIRCUMSTANCES should you ask Siri questions. Keep your gaze to the horizon. AVOID ALL CONTACT. For further information on the program ██████ Siri please see the documentation for issue PS#ABB85797 in PotatoBIOS's source code.

104
build.py
View File

@ -1,104 +0,0 @@
#!/usr/bin/env python3
import hashlib
import json
import datetime
import shutil
import ccecc
import argparse
from pathlib import Path, PurePosixPath
import sys
import subprocess
import os
parser = argparse.ArgumentParser(description="build potatOS")
parser.add_argument("-D", "--description", help="description of version")
parser.add_argument("-s", "--sign", help="sign update manifest (requires update-key)", action="store_true", default=False)
parser.add_argument("-m", "--minify", help="minify (production build)", action="store_true", default=False)
args = parser.parse_args()
workdir = Path(sys.argv[0]).parent.resolve()
src = workdir / "src"
dist = workdir / "dist"
shutil.rmtree(dist)
os.makedirs(dist, exist_ok=True)
shutil.copy(src / "polychoron.lua", dist / "startup")
for x in ["xlib", "signing-key.tbl", "LICENSES", "stdlib.hvl", "bin", "potatobios.lua"]:
if (src / x).is_dir(): shutil.copytree(src / x, dist / x)
else: shutil.copy(src / x, dist / x)
proc = subprocess.run(["npx", "luabundler", "bundle", src / "main.lua", "-p", src / "lib" / "?.lua"], capture_output=True)
proc.check_returncode()
with open(dist / "autorun.lua", "wb") as f:
f.write(proc.stdout.rstrip())
if args.minify:
os.chdir(workdir / "minify")
for x in ["autorun.lua", "potatobios.lua"]:
file = dist / x
subprocess.run(["lua5.1", "CommandLineMinify.lua", file, file.with_suffix(".lua.tmp"), file.with_suffix(".lua.map")]).check_returncode()
file.with_suffix(".lua.tmp").rename(file)
os.chdir(workdir)
subprocess.run(["sed", "-i", "19iif _G.package and _G.package.loaded[package] then loadedModule = _G.package.loaded[package] end if _G.package and _G.package.preload[package] then local pkg = _G.package.preload[package](_G.package) _G.package.loaded[package] = pkg loadedModule = pkg end", dist / "autorun.lua"]).check_returncode()
with open(dist / "autorun.lua", "a") as f:
f.write("(...)")
counter = 0
manifest_path = workdir / "manifest"
if manifest_path.exists():
current = open(manifest_path).read().split("\n")[0]
counter = json.loads(current).get("build", 0)
def hash_file(path):
file = open(path, "rb")
h = hashlib.sha256()
count = 0
while data := file.read(65536):
h.update(data)
count += len(data)
return h.hexdigest(), count
if args.sign:
print("Signing update")
import genkeys
k = genkeys.get_key()
pubkey = ccecc.public_key(k).hex()
open("dist/update-key.hex", "w").write(pubkey)
files = dict()
sizes = dict()
code = Path("./dist/")
for path in code.glob("**/*"):
if not path.is_dir() and not path.parts[-1].endswith(".map"):
hexhash, count = hash_file(path)
mpath = "/".join(path.parts[1:])
files[mpath] = hexhash
sizes[mpath] = count
def deterministic_json_serialize(x):
return json.dumps(x, sort_keys=True, separators=(",", ":"))
manifest_data = deterministic_json_serialize({
"files": files,
"sizes": sizes,
"timestamp": int(datetime.datetime.now().timestamp()),
"build": counter + 1,
"description": args.description
})
manifest_meta = {
"hash": hashlib.sha256(manifest_data.encode("utf-8")).hexdigest()
}
if args.sign:
manifest_meta["sig"] = ccecc.sign(k, manifest_meta["hash"].encode("utf-8")).hex()
manifest_meta = deterministic_json_serialize(manifest_meta)
manifest = f"{manifest_data}\n{manifest_meta}"
open(manifest_path, "w").write(manifest)
shutil.copy(manifest_path, dist)
print(counter + 1)

21
build.sh Executable file
View File

@ -0,0 +1,21 @@
#!/bin/sh
mkdir -p dist
rm -r dist/*
cp src/polychoron.lua dist/startup
cp -r src/xlib/ dist
cp -r src/signing-key.tbl dist
cp -r src/LICENSES dist
cp -r src/stdlib.hvl dist
cp -r src/bin/ dist
cp src/potatobios.lua dist/
npx luabundler bundle src/main.lua -p "src/lib/?.lua" | perl -pe 'chomp if eof' > dist/autorun_full.lua
WORK=$(pwd)
cd ./minify
lua5.1 CommandLineMinify.lua "$WORK/dist/autorun_full.lua" "$WORK/dist/autorun.lua" "$WORK/dist/autorun.lua.map"
lua5.1 CommandLineMinify.lua "$WORK/dist/potatobios.lua" "$WORK/dist/pb_tmp.lua" "$WORK/dist/potatobios.lua.map"
mv "$WORK/dist/pb_tmp.lua" "$WORK/dist/potatobios.lua"
cd "$WORK"
rm dist/autorun_full.lua
sed -i '19iif _G.package and _G.package.loaded[package] then loadedModule = _G.package.loaded[package] end if _G.package and _G.package.preload[package] then local pkg = _G.package.preload[package](_G.package) _G.package.loaded[package] = pkg loadedModule = pkg end' dist/autorun.lua
echo -n "(...)" >> dist/autorun.lua
./generate_manifest.py "$@"

View File

@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark light">
<title>404 | Copy Cat</title>
<link rel="stylesheet" href="/main.css?v=be620c97" />
</head>
<body>
<div id="page" class="container">
<div class="infoContainer">
<div class="infoView">
<h1>404 - Page not found</h1>
<p>
Truth be told, the only page worth looking into here is <a href="/" title="The home page">the home page</a>.
</p>
<p>
If you were expecting something to be here but it wasn't, why not
<a href="https://github.com/SquidDev-CC/copy-cat" title="The issue tracker">file a bug</a>?
</p>
</div>
</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,845 +0,0 @@
Name: @squid-dev/cc-web-term
Version: 2.0.1
License: BSD-3-Clause
Private: false
Description: A ComputerCraft terminal for the internet
Repository: git+https://github.com/SquidDev-CC/cc-web-term.git
Homepage: https://github.com/SquidDev-CC/cc-web-term#readme
Author: SquidDev <squid@squiddev.cc>
License Copyright:
===
Copyright (c) 2020 SquidDev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of hydraz, squiddev nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
Name: preact
Version: 10.18.1
License: MIT
Private: false
Description: Fast 3kb React-compatible Virtual DOM library.
Repository: undefined
Homepage: https://preactjs.com
License Copyright:
===
The MIT License (MIT)
Copyright (c) 2015-present Jason Miller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
Name: tslib
Version: 2.6.2
License: 0BSD
Private: false
Description: Runtime library for TypeScript helper functions
Repository: https://github.com/Microsoft/tslib.git
Homepage: https://www.typescriptlang.org/
Author: Microsoft Corp.
License Copyright:
===
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
---
Name: style-inject
Version: 0.3.0
License: MIT
Private: false
Description: Inject style tag to document head.
Repository: git+https://github.com/egoist/style-inject.git
Homepage: https://github.com/egoist/style-inject#readme
Author: EGOIST <0x142857@gmail.com>
License Copyright:
===
The MIT License (MIT)
Copyright (c) 2015 egoist 0x142857@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---
Name: setimmediate
Version: 1.0.5
License: MIT
Private: false
Description: A shim for the setImmediate efficient script yielding API
Repository: undefined
Author: YuzuJS
Contributors:
Domenic Denicola <d@domenic.me> (https://domenic.me)
Donavon West <github@donavon.com> (http://donavon.com)
Yaffle
License Copyright:
===
Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
Name: jszip
Version: 3.10.1
License: (MIT OR GPL-3.0-or-later)
Private: false
Description: Create, read and edit .zip files with JavaScript http://stuartk.com/jszip
Repository: https://github.com/Stuk/jszip.git
Author: Stuart Knightley <stuart@stuartk.com>
Contributors:
Franz Buchinger
António Afonso
David Duponchel
yiminghe
License Copyright:
===
JSZip is dual licensed. At your choice you may use it under the MIT license *or* the GPLv3
license.
The MIT License
===============
Copyright (c) 2009-2016 Stuart Knightley, David Duponchel, Franz Buchinger, António Afonso
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
GPL version 3
=============
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS

View File

@ -1,6 +0,0 @@
/**
* copy-cat: Copyright SquidDev 2023
*
*
* @license
*/define(["./persist-7dd7de50","require"],function(e,t){"use strict";let r={addString:()=>{},addBoolean:()=>{},addInt:()=>{}};class o extends e.b{constructor(r,o){var n,l;super(r,o);let{persistId:i,hdFont:s}=r,a=new e.TerminalData,u=new e.Semaphore,c=new e.ComputerAccess(void 0===i?new e.VoidPersistence:new e.StoragePersistence(i),a,u,(e,t)=>this.setState({label:e,on:t}));null===(n=r.resolve)||void 0===n||n.call(r,c);let p="string"==typeof s?s:t.toUrl("./"+(void 0===s||s?e.termFontHd:e.termFont)),d=null!==(l=r.files)&&void 0!==l?l:{};for(let t in d){if(!Object.prototype.hasOwnProperty.call(d,t))continue;let[r]=e.splitName(t);if(r){let e=c.createDirectory(r);if(null===e.value)throw Error(e.error)}let o=d[t],n=c.createFile(t);if(null===n.value)throw Error(n.error);let l=n.value.setContents(o);if(null===l.value)throw Error(l.error)}let m=r.peripherals;if(m)for(let e in m){if(!Object.prototype.hasOwnProperty.call(m,e))continue;let t=m[e];null!=t&&c.setPeripheral(e,t)}this.setState({on:!1,label:null,font:p,terminal:a,terminalChanged:u,computer:c})}componentDidMount(){this.state.computer.start(()=>r,this.props)}componentWillUnmount(){this.state.computer.dispose()}render(t,{font:r,computer:o,terminal:n,terminalChanged:l,label:i,on:s}){return e.y(e.Terminal,{terminal:n,changed:l,focused:!0,computer:o,font:r,id:0,label:i,on:s})}}let n=(t,r)=>new Promise((n,l)=>e.B(e.y(o,Object.assign({resolve:n},null!=r?r:{})),t));return n.h=e.y,n.Component=e.b,n.render=e.B,n.Computer=o,n});

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
/**
* copy-cat: Copyright SquidDev 2023
*
* - @squid-dev/cc-web-term: Copyright SquidDev (BSD-3-Clause)
* - jszip: Copyright Stuart Knightley ((MIT OR GPL-3.0-or-later))
* - preact: Copyright (MIT)
* - setimmediate: Copyright YuzuJS (MIT)
* - style-inject: Copyright EGOIST (MIT)
* - tslib: Copyright Microsoft Corp. (0BSD)
*
* @license
*/define(["exports","vs/editor/editor.main","./persist-7dd7de50"],function(e,o,l){"use strict";let n;o.languages.register({id:"luax",aliases:["LuaX","LuaX","luax"],extensions:[".lua"]}),o.languages.setLanguageConfiguration("luax",{comments:{lineComment:"--",blockComment:["--[[","]]"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:'"',close:'"'}],indentationRules:{increaseIndentPattern:/((\b(else|function|then|do|repeat)\b((?!\b(end|until)\b).)*)|(\{\s*))$/,decreaseIndentPattern:/^\s*((\b(elseif|else|end|until)\b)|(\})|(\)))/}}),o.languages.setMonarchTokensProvider("luax",{defaultToken:"",tokenPostfix:".lua",keywords:["and","break","do","else","elseif","end","false","for","function","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"],brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],operators:["+","-","*","/","%","^","#","==","~=","<=",">=","<",">","=",";",":",",",".","..","..."],symbols:/[=><!~?:&|+\-*/^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/(,)(\s*)([a-zA-Z_]\w*)(\s*)(:)(?!:)/,["delimiter","","key","","delimiter"]],[/({)(\s*)([a-zA-Z_]\w*)(\s*)(:)(?!:)/,["@brackets","","key","","delimiter"]],[/[{}()[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\.\d+([eE][-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F_]*[0-9a-fA-F]/,"number.hex"],[/\d+?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'/,"string2","@string.'"],[/"/,"string",'@string."']],whitespace:[[/[ \t\r\n]+/,""],[/--\[([=]*)\[/,"comment","@comment.$1"],[/--.*$/,"comment"]],comment:[[/[^\]]+/,"comment"],[/\]([=]*)\]/,{cases:{"$1==$S2":{token:"comment",next:"@pop"},"@default":"comment"}}],[/./,"comment"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}});let r=()=>null!=n?n:n=fetch("https://tweaked.cc/index.json").then(e=>e.json()).catch(e=>(console.error("Failed to fetch index",e),null)),t=e=>l.__awaiter(void 0,void 0,void 0,function*(){var o;if(e.match(/\.[A-Z]/))return null;let l=yield r();return l?null!==(o=l[e])&&void 0!==o?o:l[`_G.${e}`]:null}),s=/[A-za-z_][\w.]*$/,i=/^[\w.]*/;o.languages.registerHoverProvider("luax",{provideHover:(e,n)=>l.__awaiter(void 0,void 0,void 0,function*(){var l,r,a,c;let u=e.getLineContent(n.lineNumber),d=null!==(r=null===(l=u.substring(0,n.column).match(s))||void 0===l?void 0:l[0])&&void 0!==r?r:"",m=null!==(c=null===(a=u.substring(n.column).match(i))||void 0===a?void 0:a[0])&&void 0!==c?c:"",g=d+m;if(!g)return;let p=yield t(g);if(!p||"module"!=p["module-kind"])return null;let f=[{value:`\`${p.name}\``}];return p.summary&&f.push({value:p.summary}),f.push({value:`[View full documentation](https://tweaked.cc/${p.url})`}),{range:new o.Range(n.lineNumber,n.column-d.length,n.lineNumber,n.column+m.length),contents:f}})});let a=e=>({red:(e>>16&255)/255,green:(e>>8&255)/255,blue:(255&e)/255,alpha:1}),c={"colors.white":a(15790320),"colors.orange":a(15905331),"colors.magenta":a(15040472),"colors.lightBlue":a(10072818),"colors.yellow":a(14605932),"colors.lime":a(8375321),"colors.pink":a(15905484),"colors.gray":a(5000268),"colors.lightGray":a(10066329),"colors.cyan":a(5020082),"colors.purple":a(11691749),"colors.blue":a(3368652),"colors.brown":a(8349260),"colors.green":a(5744206),"colors.red":a(13388876),"colors.black":a(1118481),"colours.white":a(15790320),"colours.orange":a(15905331),"colours.magenta":a(15040472),"colours.lightBlue":a(10072818),"colours.yellow":a(14605932),"colours.lime":a(8375321),"colours.pink":a(15905484),"colours.grey":a(5000268),"colours.lightGrey":a(10066329),"colours.cyan":a(5020082),"colours.purple":a(11691749),"colours.blue":a(3368652),"colours.brown":a(8349260),"colours.green":a(5744206),"colours.red":a(13388876),"colours.black":a(1118481)};o.languages.registerColorProvider("luax",{provideColorPresentations:()=>[],provideDocumentColors:e=>{let o=[];for(let{range:l}of e.findMatches("colou?rs\\.\\w+",!1,!0,!0,"()[]{}<>`'\"-/;:,.?!",!1)){let n=c[e.getValueInRange(l)];n&&o.push({color:n,range:l})}return o}}),Object.keys(o).forEach(l=>{"default"===l||Object.prototype.hasOwnProperty.call(e,l)||Object.defineProperty(e,l,{enumerable:!0,get:()=>o[l]})})});

View File

@ -1,12 +0,0 @@
/**
* copy-cat: Copyright SquidDev 2023
*
* - @squid-dev/cc-web-term: Copyright SquidDev (BSD-3-Clause)
* - jszip: Copyright Stuart Knightley ((MIT OR GPL-3.0-or-later))
* - preact: Copyright (MIT)
* - setimmediate: Copyright YuzuJS (MIT)
* - style-inject: Copyright EGOIST (MIT)
* - tslib: Copyright Microsoft Corp. (0BSD)
*
* @license
*/define(["exports","vs/editor/editor.main","./persist-b71da708"],function(e,o,l){"use strict";let n;o.languages.register({id:"luax",aliases:["LuaX","LuaX","luax"],extensions:[".lua"]}),o.languages.setLanguageConfiguration("luax",{comments:{lineComment:"--",blockComment:["--[[","]]"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:'"',close:'"'}],indentationRules:{increaseIndentPattern:/((\b(else|function|then|do|repeat)\b((?!\b(end|until)\b).)*)|(\{\s*))$/,decreaseIndentPattern:/^\s*((\b(elseif|else|end|until)\b)|(\})|(\)))/}}),o.languages.setMonarchTokensProvider("luax",{defaultToken:"",tokenPostfix:".lua",keywords:["and","break","do","else","elseif","end","false","for","function","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"],brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],operators:["+","-","*","/","%","^","#","==","~=","<=",">=","<",">","=",";",":",",",".","..","..."],symbols:/[=><!~?:&|+\-*/^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/(,)(\s*)([a-zA-Z_]\w*)(\s*)(:)(?!:)/,["delimiter","","key","","delimiter"]],[/({)(\s*)([a-zA-Z_]\w*)(\s*)(:)(?!:)/,["@brackets","","key","","delimiter"]],[/[{}()[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\.\d+([eE][-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F_]*[0-9a-fA-F]/,"number.hex"],[/\d+?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'/,"string2","@string.'"],[/"/,"string",'@string."']],whitespace:[[/[ \t\r\n]+/,""],[/--\[([=]*)\[/,"comment","@comment.$1"],[/--.*$/,"comment"]],comment:[[/[^\]]+/,"comment"],[/\]([=]*)\]/,{cases:{"$1==$S2":{token:"comment",next:"@pop"},"@default":"comment"}}],[/./,"comment"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}});let r=()=>null!=n?n:n=fetch("https://tweaked.cc/index.json").then(e=>e.json()).catch(e=>(console.error("Failed to fetch index",e),null)),t=e=>l.__awaiter(void 0,void 0,void 0,function*(){var o;if(e.match(/\.[A-Z]/))return null;let l=yield r();return l?null!==(o=l[e])&&void 0!==o?o:l[`_G.${e}`]:null}),s=/[A-za-z_][\w.]*$/,i=/^[\w.]*/;o.languages.registerHoverProvider("luax",{provideHover:(e,n)=>l.__awaiter(void 0,void 0,void 0,function*(){var l,r,a,c;let u=e.getLineContent(n.lineNumber),d=null!==(r=null===(l=u.substring(0,n.column).match(s))||void 0===l?void 0:l[0])&&void 0!==r?r:"",m=null!==(c=null===(a=u.substring(n.column).match(i))||void 0===a?void 0:a[0])&&void 0!==c?c:"",g=d+m;if(!g)return;let p=yield t(g);if(!p||"module"!=p["module-kind"])return null;let f=[{value:`\`${p.name}\``}];return p.summary&&f.push({value:p.summary}),f.push({value:`[View full documentation](https://tweaked.cc/${p.url})`}),{range:new o.Range(n.lineNumber,n.column-d.length,n.lineNumber,n.column+m.length),contents:f}})});let a=e=>({red:(e>>16&255)/255,green:(e>>8&255)/255,blue:(255&e)/255,alpha:1}),c={"colors.white":a(15790320),"colors.orange":a(15905331),"colors.magenta":a(15040472),"colors.lightBlue":a(10072818),"colors.yellow":a(14605932),"colors.lime":a(8375321),"colors.pink":a(15905484),"colors.gray":a(5000268),"colors.lightGray":a(10066329),"colors.cyan":a(5020082),"colors.purple":a(11691749),"colors.blue":a(3368652),"colors.brown":a(8349260),"colors.green":a(5744206),"colors.red":a(13388876),"colors.black":a(1118481),"colours.white":a(15790320),"colours.orange":a(15905331),"colours.magenta":a(15040472),"colours.lightBlue":a(10072818),"colours.yellow":a(14605932),"colours.lime":a(8375321),"colours.pink":a(15905484),"colours.grey":a(5000268),"colours.lightGrey":a(10066329),"colours.cyan":a(5020082),"colours.purple":a(11691749),"colours.blue":a(3368652),"colours.brown":a(8349260),"colours.green":a(5744206),"colours.red":a(13388876),"colours.black":a(1118481)};o.languages.registerColorProvider("luax",{provideColorPresentations:()=>[],provideDocumentColors:e=>{let o=[];for(let{range:l}of e.findMatches("colou?rs\\.\\w+",!1,!0,!0,"()[]{}<>`'\"-/;:,.?!",!1)){let n=c[e.getValueInRange(l)];n&&o.push({color:n,range:l})}return o}}),Object.keys(o).forEach(l=>{"default"===l||Object.prototype.hasOwnProperty.call(e,l)||Object.defineProperty(e,l,{enumerable:!0,get:()=>o[l]})})});

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark light">
<title>Copy Cat</title>
<link rel="stylesheet" href="main.css?v=be620c97" />
</head>
<body>
<div id="page" class="container">
<div class="infoContainer">
<div class="infoView">
<h1>Copy Cat</h1>
<p>Please wait one moment: we're just getting things set up.</p>
<p>If this message doesn't go away, something has gone horrifically wrong
- have you got JavaScript disabled?</p>
</div>
</div>
</div>
<script type="text/javascript" data-main="main.js?v=be620c97" src="require.js?v=be620c97"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,38 +0,0 @@
body {
line-height: 1.3em;
color: #444;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
html, body, .container {
height: 100vh;
width: 100%;
margin: 0px;
padding: 0px;
border: none;
}
pre, code {
font-family: "Consolas", "Courier New", monospace;
background-color: #f5f5f5;
}
pre {
padding: 10px;
overflow-x: scroll;
}
.infoContainer {
position: relative;
top: 80px;
margin: 0px auto;
max-width: 800px;
}
.infoView {
padding: 5px 10px;
font-size: 1.5em;
line-height: 1.3em;
border: 3px solid #eee;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

71
generate_manifest.py Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python3
import hashlib
import json
import datetime
import os.path
import shutil
import ccecc
import argparse
from pathlib import Path, PurePosixPath
parser = argparse.ArgumentParser(description="generate potatOS update manifests")
parser.add_argument("-D", "--description", help="description of version")
parser.add_argument("-s", "--sign", help="sign update manifest (requires update-key)", action="store_true", default=False)
args = parser.parse_args()
counter = 0
if os.path.exists("./manifest"):
current = open("manifest").read().split("\n")[0]
counter = json.loads(current).get("build", 0)
def hash_file(path):
file = open(path, "rb")
h = hashlib.sha256()
count = 0
while data := file.read(65536):
h.update(data)
count += len(data)
return h.hexdigest(), count
if args.sign:
print("Signing update")
import genkeys
k = genkeys.get_key()
pubkey = ccecc.public_key(k).hex()
open("dist/update-key.hex", "w").write(pubkey)
files = dict()
sizes = dict()
code = Path("./dist/")
for path in code.glob("**/*"):
if not path.is_dir() and not path.parts[-1].endswith(".map"):
hexhash, count = hash_file(path)
mpath = "/".join(path.parts[1:])
files[mpath] = hexhash
sizes[mpath] = count
def deterministic_json_serialize(x):
return json.dumps(x, sort_keys=True, separators=(",", ":"))
manifest_data = deterministic_json_serialize({
"files": files,
"sizes": sizes,
"timestamp": int(datetime.datetime.now().timestamp()),
"build": counter + 1,
"description": args.description
})
manifest_meta = {
"hash": hashlib.sha256(manifest_data.encode("utf-8")).hexdigest()
}
if args.sign:
manifest_meta["sig"] = ccecc.sign(k, manifest_meta["hash"].encode("utf-8")).hex()
manifest_meta = deterministic_json_serialize(manifest_meta)
manifest = f"{manifest_data}\n{manifest_meta}"
open("manifest", "w").write(manifest)
shutil.copy("manifest", "dist")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -35,20 +35,9 @@ ul p, ol p {
margin: 0;
}
img, video {
img {
width: 100%;
}
button {
width: 100%;
border: 1px solid gray;
padding: 1em;
}
#computer {
width: 100%;
border: none;
}
"""
def privacy_policy():
@ -92,81 +81,24 @@ def privacy_policy():
"""
script = open("privacy/script.js", "r").read()
mdtext = cmarkgfm.markdown_to_html_with_extensions(out, cmarkgfmOptions.CMARK_OPT_FOOTNOTES | cmarkgfmOptions.CMARK_OPT_UNSAFE)
return f"""<!DOCTYPE html><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="The privacy policy of PotatOS."><title>PotatOS Privacy Policy</title><style>{local_css}</style>\n{mdtext}<div id=contentend></div><script>{script}</script>"""
return f"""<!DOCTYPE html><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>PotatOS Privacy Policy</title><style>{local_css}</style>\n{mdtext}<div id=contentend></div><script>{script}</script>"""
with open("README.md") as f:
html = commonmark.commonmark("\n".join(f.read().splitlines()[1:]))
gif_replacer = f"""
const randpick = xs => xs[Math.floor(Math.random() * xs.length)]
const im = document.getElementById("im")
const vids = {json.dumps(os.listdir("images/front"))}
const vids = {json.dumps(os.listdir("images"))}.filter(x => !x.endsWith(".gif"))
if (Math.random() < 0.02) {{
const v = document.createElement("video")
v.src = "/front/" + randpick(vids)
v.src = vids[Math.floor(Math.random() * vids.length)]
v.muted = true
v.loop = true
v.autoplay = true
im.replaceWith(v)
}}
Array.from(document.querySelectorAll("script")).forEach(x => x.parentElement.removeChild(x))
const threat = {json.dumps(os.listdir("images/threat-updates"))}
document.querySelector("#threat-update").src = "/threat-updates/" + randpick(threat)
const demoButton = document.querySelector("#launch-demo")
demoButton.addEventListener("click", () => {{
const node = document.createElement("iframe")
node.src = "/computer.html"
node.id = "computer"
demoButton.parentNode.parentNode.insertBefore(node, demoButton.parentNode.nextSibling)
demoButton.remove()
window.addEventListener("message", e => {{
document.querySelector("#computer").style.height = `${{e.data}}px`
}})
}})
"""
computer_html = """<!DOCTYPE html>
<style>
#computer {
width: 100%;
}
</style>
<link rel="stylesheet" href="/copy-cat/main.css" />
<div id="computer"></div>
<script type="text/javascript" src="/copy-cat/require.js"></script>
<script>
const doScaler = () => {
const w = window.innerWidth
const ar = 1.7541899441340782
const canvas = document.querySelector("canvas")
canvas.style.width = `${w}px`
canvas.style.height = `${w/ar}px`
canvas.parentNode.style.width = `${w}px`
window.top.postMessage(document.querySelector("#computer").getBoundingClientRect().height, "*")
}
require.config({ paths: { copycat: "/copy-cat/" } });
require(["copycat/embed"], setup => {
window.setup = setup
const computer = setup(document.getElementById("computer"), {
persistId: 0,
hdFont: false,
files: {
"startup.lua": `settings.set("potatOS.distribution_server", "https://osmarks.net/stuff/potatos/manifest")
shell.run "wget run https://osmarks.net/stuff/potatos/autorun.lua"`,
},
label: "PotatOS",
}).then(x => {
console.log(x)
setInterval(doScaler, 100) // sorry
})
});
window.addEventListener("resize", doScaler)
</script>
"""
with open("website/computer.html", "w") as f:
f.write(computer_html)
with open("manifest", "r") as f:
data = f.readlines()
main = json.loads(data[0])
@ -180,7 +112,6 @@ html = f"""
<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="PotatOS Otiose Transformative Advanced Technology Or Something, inescapably, is the best OS for ComputerCraft and derivatives. Install now with pastebin run 7HSiHybr.">
<title>PotatOS</title>
<style>{css}</style>
<h1>Welcome to PotatOS!</h1>
@ -192,15 +123,8 @@ html = f"""
os.makedirs("website/privacy", exist_ok=True)
for im in os.listdir("images"):
src, dst = os.path.join("images", im), os.path.join("website", im)
if os.path.isdir(src):
if os.path.exists(dst): shutil.rmtree(dst)
shutil.copytree(src, dst)
else:
shutil.copy(src, dst)
shutil.copy(os.path.join("images", im), os.path.join("website", im))
with open("website/index.html", "w") as f:
f.write(html)
with open("website/privacy/index.html", "w") as f:
f.write(privacy_policy())
if os.path.exists("website/copy-cat"): shutil.rmtree("website/copy-cat")
shutil.copytree("copy-cat", "website/copy-cat")
f.write(privacy_policy())

File diff suppressed because one or more lines are too long

View File

@ -1,260 +0,0 @@
--[[
PotatOS OmniDisk
A new system to unify the existing PotatOS Uninstall/Debug/Update disks currently in existence.
Comes with a flexible, modular design, centralized licensing, possibly neater code, and a menu.
This is designed to be executed by the OmniDisk Loader (https://pastebin.com/S1RS76pv) but may run on its own, though this is NOT a supported configuration.
This is NOT usable simply by copying it onto a disk due to PotatOS signing requirements.
You must use the dcopy (https://pastebin.com/TfNgRUKC) program or manually generate a hex-format ECC signature and write it to "disk/signature" (PotatOS will, however, not run it unless this signature is from the PDSK).
]]
local function try_report_incident(...)
if _G.report_incident then
_G.report_incident(...)
print "This incident has been reported."
end
end
local r = process.get_running()
local sandbox = process.info "sandbox"
if sandbox then
for _, p in pairs(process.list()) do
if p.parent == sandbox and p.ID ~= r.ID then
process.signal(p.ID, process.signals.KILL)
end
end
end
pcall(process.signal, "sandbox", process.signals.KILL)
os.queueEvent "stop"
local function fetch(URL)
local h, e = http.get(URL)
if not h then error(e) end
local o = h.readAll()
h.close()
return o
end
local UUID = "@UUID@" -- Populated by dcopy utility, in some setups
local args = ...
if type(args) == "table" and args.UUID then UUID = args.UUID end
local json
if _G.json_for_disks_and_such then json = _G.json_for_disks_and_such
elseif textutils.unserialiseJSON then
json = { encode = textutils.serialiseJSON, decode = textutils.unserialiseJSON }
else error "No JSON library exists, somehow" end
local license_data = fetch "https://pastebin.com/raw/viz0spjb"
local licenses = json.decode(license_data)
local license = licenses[UUID]
local disk_ID
local disk_loader_args = args.arguments
if type(disk_loader_args) == "table" and disk_loader_args.ID then
disk_ID = disk_loader_args.ID
end
local function runfile(program, ...)
local ok, err = loadfile(program)
if not ok then error(err) end
ok(...)
end
local features = {
test = {
fn = function() print "Hello, World!" end,
description = "Test function."
},
exit = {
fn = function() os.reboot() end,
description = "Leave OmniDisk, return to PotatOS.",
always_permitted = true
},
UUID = {
fn = function() print("UUID:", UUID) print("Disk ID:", disk_ID or "[???]") end,
description = "Print this OmniDisk's Licensing UUID.",
always_permitted = true
},
uninstall = {
fn = function() print "Uninstalling..." _G.uninstall "omnidisk" end,
description = "Uninstall potatOS"
},
REPL = {
fn = function() runfile("/rom/programs/shell.lua", "lua") end,
description = "Open a Lua REPL for debugging."
},
shell = {
fn = function()
printError "WARNING!"
print "Do not attempt to modify the code of this PotatOS OmniDisk. Unauthorized attempts to do so will invalidate the signature and make the disk unusable. All code beyond a limited core is stored in an online file to which you do not have write access. Probably. Contact gollark for further information."
runfile "/rom/programs/shell.lua"
end,
description = "Open an unsandboxed shell."
},
update = {
fn = function() runfile("autorun", "update") end,
description = "Update PotatOS."
},
dump_license = {
fn = function() print(UUID) textutils.pagedPrint(textutils.serialise(license)) end,
description = "Dump license information."
},
primes = {
fn = function()
if not _G.findprime or not _G.isprime then
error "findprime/isprime not available. Update potatOS."
end
write "Difficulty? (1-16) "
local difficulty = tonumber(read())
if type(difficulty) ~= "number" then error "ERR_PEBKAC\nThat's not a number." end
local maxrand = math.pow(10, difficulty)
local p1 = findprime(math.random(2, maxrand))
local p2 = findprime(math.random(2, maxrand))
local num = p1 * p2
print("Please find the prime factors of the following number:", num)
write "Factor 1: "
local f1 = tonumber(read())
write "Factor 2: "
local f2 = tonumber(read())
if (f1 == p1 and f2 == p2) or (f2 == p1 and f1 == p2) then
print "Yay! You got it right!"
else
print("Factors", f1, f2, "invalid.", p1, p2, "expected.")
end
end,
description = "Bored? You can factor some semiprimes!"
},
potatoplex = {
fn = function()
write "Run potatoplex with arguments: "
local args = read()
runfile("rom/programs/http/pastebin.lua", "run", "wYBZjQhN", args)
end,
description = "Potatoplex your life!"
},
chronometer = {
fn = function()
runfile("rom/programs/http/pastebin.lua", "run", "r24VMWk4")
end,
description = "Tell the time with Chronometer!"
},
latest_paste = {
fn = function()
write "WARNING: This views the latest paste on Pastebin. Exposure to the raw output of the Internet may be detrimental to your mental health. Do you want to continue (y/n)? "
local yn = read()
if not yn:lower():match "y" then return end
local html = fetch "https://pastebin.com/LW9RFpmY"
local id = html:match [[<ul class="right_menu"><li><a href="/([A-Za-z0-9]+)">]]
local url = ("https://pastebin.com/raw/%s"):format(id)
local title = html:match [[<ul class="right_menu"><li><a href="/[A-Za-z0-9]+">([^<]+)</a>]]
local content = fetch(url)
term.clear()
term.setCursorPos(1, 1)
textutils.pagedPrint(title .. "\n" .. url .. "\n\n" .. content)
end,
description = "View latest paste on Pastebin."
}
}
local function wait()
write "Press Any key to continue."
os.pullEvent "key"
local timer = os.startTimer(0)
while true do
local e, arg = os.pullEvent()
if (e == "timer" and arg == timer) or e == "char" then return end
end
end
if not license then
printError(([[ERR_NO_LICENSE
This disk (UUID %s) does not have an attached license and is invalid.
This should not actually happen, unless you have meddled with the disk while somehow keeping the signature intact.
Please contact gollark.]]):format(tostring(UUID)))
try_report_incident(("OmniDisk UUID %s has no license data"):format(tostring(UUID)), {"security", "omnidisk"}, {
extra_meta = {
disk_ID = disk_ID,
omnidisk_UUID = UUID
}
})
wait()
os.reboot()
end
if disk_ID then
local license_ID = license.disk
local ok = false
if type(license_ID) == "table" then
for _, id in pairs(license_ID) do
if id == disk_ID then ok = true break end
end
elseif type(license_ID) == "number" then
if license_ID == disk_ID then ok = true end
else
ok = true
end
if not ok then
printError(([[ERR_WRONG_DISK
This disk (ID %d) is not (one of) the disk(s) specified in your licensing information.
This license (UUID %s) allows use of this/these disk(s): %s.
If you believe this to be in error, please contact gollark so this can be corrected.
Otherwise, stop cloning disks, or contact gollark to have unique UUIDs issued to each.]]):format(disk_ID, UUID, json.encode(license_ID)))
try_report_incident(("Disk ID mismatch: %d used with license %s"):format(disk_ID, UUID, json.encode(license_ID)), {"security", "omnidisk"}, {
extra_meta = {
permitted_disk_IDs = license_ID,
disk_ID = disk_ID,
omnidisk_UUID = UUID
}
})
wait()
os.reboot()
end
end
local permitted_feature_lookup = {}
for _, feature in pairs(license.features) do
permitted_feature_lookup[feature] = true
end
while true do
term.setCursorPos(1, 1)
term.clear()
local usable = {}
local i = 0
print [[Welcome to PotatOS OmniDisk!
Available options:]]
for name, feature in pairs(features) do
if permitted_feature_lookup["*"] or permitted_feature_lookup[name] or feature.always_permitted then
textutils.pagedPrint(("%d. %s - %s"):format(i, name, feature.description or "[no description available]"))
usable[i] = feature.fn
usable[name] = feature.fn
i = i + 1
end
end
write "Select an option: "
local option = read()
local fn
local as_num = tonumber(option)
if as_num then fn = usable[as_num] else fn = usable[option] end
if not fn then
printError(("ERR_ID_10T\nPlease select an option which actually exists.\n'%s' doesn't."):format(tostring(option)))
wait()
else
local ok, res = pcall(fn)
if not ok then
printError(res)
wait()
else
wait()
end
end
end

733
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
{
"dependencies": {
"luabundler": "^1.2.2"
}
}

View File

@ -9,14 +9,14 @@ For more information on PotatOS itself, consult [the main PotatOS documentation]
PotatOS provides Primarily Otiose Transformative Advanced Technology, Or Something ("PotatOS", "PotatOS™"), associated programs, libraries and other code ("PotatOS Potatosystems"), and PotatOS backend webservices such as SPUDNETv2/PIR, RSAPI, and PSUS ("PotatOS Services"). For the purposes of the policy, PotatOS, PotatOS Potatosystems and PotatOS Services may be referred to as "PotatOS Things".
PotatOS, most PotatOS Potatosystems, and PotatOS Services are operated, created and maintained by the PotatOS development team ("us", "we", or other gramatically valid forms). Some PotatOS Potatosystems are developed and maintained by third parties, and PotatOS, as a general purpose operating system, may interact with other organizations outside of the scope of this policy.
This privacy policy ("PotatOS Privacy Policy") sets out how we may use information, such as information gathered via PotatOS and PotatOS Services.
"PotatOS Privacy Policy" refers collectively to all the terms, conditions and notices contained or referenced in this document, and any associated documentation which may be published by us, including all past and future versions.
"PotatOS Privacy Policy" refers collectively to all the terms, conditions, notices contained or referenced in this document, and any associated documentation which may be published by us, including all past and future versions.
## Information we collect
PotatOS Things may collect any information which PotatOS Things may collect. This includes information such as:
* Information you provide. If you provide information, this may be stored and used in order to provide PotatOS™ functionality. This includes information such as settings, which are stored locally so that they can be read and utilized, and your files, if you make files, which are stored on disk and potentially in RAM so that they can be read back and displayed.
* All user input or all executed code, if some debug settings such as Protocol Epsilon and Extended Monitoring are enabled.
* All user input or all executed code, if some debug settings such as Protocol Epsilon and Extended Monitoring are enabled
* Internally generated information which may be indirectly derived from user input, such as your device's UPID[^3], some PotatOS Registry contents and system debug logs.
* ComputerCraft system configuration information and identification information, which is sent to SPUDNETv2/PIR and stored with incident reports to assist with debugging and/or handling the source of the reports.
* In certain jurisdictions, we may ask for a valid ID (from accepted countries such as Kazakhstan, the Democratic People's Republic of Korea, Sealand, the Freeish State of Gollarkia, Desmethylway, the Harmonious Jade Dragon Empire, or the Untied States) in limited circumstances. This is only for purposes.
@ -78,7 +78,7 @@ PotatOS is currently, has always been, and always will be, considered nonanomalo
By using potatOS, agreeing to be bound by these terms, misusing potatOS, installing potatOS, reading about potatOS, knowing about these terms, knowing anyone who is bound by these terms, disusing potatOS, reading these terms, or thinking of anything related to these terms, you agree to be bound by these terms both until the last stars in the universe burn out and the last black holes evaporate and retroactively, arbitrarily far into the past. This privacy policy may be updated at any time and at all times the latest revision applies.
You agree additionally to the following Unicode characters: א U+05D0 HEBREW LETTER ALEF and ⬡ U+2B21[^6] WHITE HEXAGON. A string constant may continue from one line to the next, but the exact contents of such a string are unspecified.
You agree additionally to the following Unicode characters: א U+05D0 HEBREW LETTER ALEF and ⬡ U+2B21[^6] WHITE HEXAGON. A string constant may continue from one line to the next, but the exact contents of such a string are unspecified
[^6]: Those who find collisions in hash functions cannot be trusted and may be banned at any time.
@ -86,7 +86,7 @@ Furthermore: by using PotatOS, you forfeit all claims on your soul by any deity
This policy supersedes any applicable federal, national, state, and local laws, regulations and ordinances, policies, international treaties, legal agreements, illegal agreements, or any other agreements, contracts, documents, policies, standards or content/information/statements/opinions/preferences that would otherwise apply. If any provision of this policy is found by a court (or other entity) to be unenforceable, it nevertheless remains in force. This policy is legal, not illegal, and a valid legal document in all jurisdictions. This organization is not liable and this agreement shall not be construed. We are not responsible for any issue whatsoever at all arising from use of potatOS, potatOS services, anything at all, or otherwise.
As an additional clarification to the above clause, this privacy policy supersedes and overrides the "EndOS" and "TaterOS" privacy policies and terms of service regardless of any contradictory claims they may contain. This policy also supersedes all statements made by [GEORGE](https://george.gh0.pw/) or agents thereof.
As an additional clarification to the above clause, this privacy policy supersedes and overrides the "EndOS" and "TaterOS" privacy policies and terms of service regardless of any contradictory claims it may contain. This policy also supersedes all statements made by [GEORGE](https://george.gh0.pw/) or agents thereof.
You are responsible for anything which potatOS might do to your things. You ran it. It is all your fault. <span class=spoiler>The turtle is watching you</span>. We are not liable, ethically, morally, existentially, financially or legally, for anything whatsoever.

View File

@ -11,7 +11,7 @@ train(order){this.clearPossibilities();if(order){this.order=order}if(this.type==
generateRandom(chars=15){const startingState=this.random(this.start,"array");let result=startingState;let current=startingState;let next="";for(let i=0;i<chars-this.order;i++){next=this.random(this.possibilities[current],"array");if(!next){break}result+=next;current=result.substring(result.length-this.order,result.length)}return result}
random(obj,type){if(Array.isArray(obj)&&type==="array"){const index=Math.floor(Math.random()*obj.length);return obj[index]}if(typeof obj==="object"&&type==="object"){const keys=Object.keys(obj);const index=Math.floor(Math.random()*keys.length);return keys[index]}}}
//console.log("Initiating Protocol ASCENDING CARPOOL.")
console.log("Initiating Protocol ASCENDING CARPOOL.")
const strings = document.body.innerText.split("\n").filter(x => !/^[0-9]\.[0-9]$/.exec(x)).flatMap(x => x.split("."))
const m = new Markov()
@ -60,7 +60,7 @@ const addText = () => {
const node = document.createElement("h2")
node.appendChild(document.createTextNode(title))
contentEnd.appendChild(node)
//console.log(title)
console.log(title)
for (let i = 0; i < Math.floor(Math.random() * 5 + 2); i++) {
const headerNode = document.createElement("h3")
const aNode = document.createElement("a")
@ -112,13 +112,11 @@ const update = () => {
window.addEventListener("scroll", () => {
if (Math.random() < 0.01) {
//console.log("Scheduler online. WITLESS HOROLOGISTS procedure started.")
console.log("Scheduler online. WITLESS HOROLOGISTS procedure started.")
if ("requestIdleCallback" in window) {
window.requestIdleCallback(update, { timeout: 200 })
} else {
setTimeout(update)
}
}
})
Array.from(document.querySelectorAll("script")).forEach(x => x.parentElement.removeChild(x))
})

View File

@ -1,3 +0,0 @@
local w, h = term.getSize()
polychoron.BSOD(potatOS.randbytes(math.random(0, w * h)))
os.pullEvent "key"

View File

@ -1 +0,0 @@
print "abcdefghijklmnopqrstuvwxyz"

View File

@ -1,19 +0,0 @@
print("Short hash", potatOS.build)
print("Full hash", potatOS.full_build)
local mfst = potatOS.registry.get "potatOS.current_manifest"
if mfst then
print("Counter", mfst.build)
print("Built at (local time)", os.date("%Y-%m-%d %X", mfst.timestamp))
print("Downloaded from", mfst.manifest_URL)
local verified = mfst.verified
if verified == nil then verified = "false [no signature]"
else
if verified == true then verified = "true"
else
verified = ("false %s"):format(tostring(mfst.verification_error))
end
end
print("Signature verified", verified)
else
print "Manifest not found in registry. Extended data unavailable."
end

View File

@ -1 +0,0 @@
print(potatOS.chuck_norris())

View File

@ -1 +0,0 @@
potatOS.clear_space((... and tonumber(...) and tonumber(...) == tonumber(...)) and tonumber(...) or 4096)

View File

@ -1,3 +0,0 @@
for _, info in pairs(process.list()) do
textutils.pagedPrint(("%s %f %f"):format(info.name or info.ID, info.execution_time, info.ctime))
end

View File

@ -1,25 +0,0 @@
-- edit reality to match typo in docs
function Safe_SerializeWithtextutilsDotserialize(Valuje)
local _, __ = pcall(textutils.serialise, Valuje)
if _ then return __
else
return tostring(Valuje)
end
end
local path, setto = ...
path = path or ""
if setto ~= nil then
local x, jo, jx = textutils.unserialise(setto), pcall(json.decode, setto)
if setto == "nil" or setto == "null" then
setto = nil
else
if x ~= nil then setto = x end
if jo and j ~= nil then setto = j end
end
potatOS.registry.set(path, setto)
print(("Value of registry entry %s set to:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(setto)))
else
textutils.pagedPrint(("Value of registry entry %s is:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(potatOS.registry.get(path))))
end

View File

@ -1,8 +0,0 @@
-- like delete but COOLER and LATIN
for _, wcard in pairs{...} do
for _, path in pairs(fs.find(wcard)) do
fs.ultradelete(path)
local n = potatOS.lorem():gsub("%.", " " .. path .. ".")
print(n)
end
end

View File

@ -12,10 +12,9 @@ repeat
write "Provide an integer to factorize: "
x = tonumber(read())
if not x or math.floor(x) ~= x then print("That is NOT an integer.") end
if x and x < 2 then print("I forgot to mention this, but also don't use 1, 0 or negative integers.") end
until x
if x > (2^40) then print("WARNING: Number is quite big. Due to Lua floating point limitations, draconic entities MAY be present and results may be blatantly wrong. If this runs for several seconds, it's probably frozen due to this.") end
if x > (2^40) then print("WARNING: Number is quite big. Due to Lua floating point limitations, draconic entities MAY be present. If this runs for several seconds, it's probably frozen due to this.") end
local floor, abs, random, log, pow = math.floor, math.abs, math.random, math.log, math.pow
@ -29,16 +28,17 @@ local function eps_compare(x, y)
return abs(x - y) < 1e-14
end
-- binary modular exponentiation
local function modexp(a, b, n)
if b == 0 then return 1 % n end
if b == 1 then return a % n end
local bdiv2 = b / 2
local fbdiv2 = floor(bdiv2)
if eps_compare(bdiv2, fbdiv2) then
-- b is even, so it is possible to just modexp with HALF the exponent and square it (mod n)
local x = modexp(a, fbdiv2, n)
return (x * x) % n
else
-- not even, so subtract 1 (this is even), modexp that, and multiply by a again (mod n)
return (modexp(a, b - 1, n) * a) % n
end
end

View File

@ -1 +0,0 @@
print(potatOS.fortune())

View File

@ -1,8 +0,0 @@
potatOS.evilify()
print "GAME KEYBOARD enabled."
potatOS.init_screens()
print "GAME SCREEN enabled."
print "Activated GAME MODE."
--bigfont.bigWrite "GAME MODE."
--local x, y = term.getCursorPos()
--term.setCursorPos(1, y + 3)

View File

@ -1,175 +0,0 @@
local mat = term.current()
mat.setPaletteColor(colors.black, 0)
mat.setPaletteColor(colors.green, 0x0cff0c)
mat.setPaletteColor(colors.red, 0xff0000)
if mat.setTextScale then mat.setTextScale(0.5) end
mat.setTextColor(colors.green)
term.redirect(mat)
local jargonWords = {
acronyms =
{"TCP", "HTTP", "SDD", "RAM", "GB", "CSS", "SSL", "AGP", "SQL", "FTP", "PCI", "AI", "ADP",
"RSS", "XML", "EXE", "COM", "HDD", "THX", "SMTP", "SMS", "USB", "PNG", "PHP", "UDP",
"TPS", "RX", "ASCII", "CD-ROM", "CGI", "CPU", "DDR", "DHCP", "BIOS", "IDE", "IP", "MAC",
"MP3", "AAC", "PPPoE", "SSD", "SDRAM", "VGA", "XHTML", "Y2K", "GUI", "EPS", "SATA", "SAS",
"VM", "LAN", "DRAM", "L3", "L2", "DNS", "UEFI", "UTF-8", "DDOS", "HDMI", "GPU", "RSA", "AES",
"L7", "ISO", "HTTPS", "SSH", "SIMD", "GNU", "PDF", "LPDDR5", "ARM", "RISC", "CISC", "802.11",
"5G", "LTE", "3GPP", "MP4", "2FA", "RCE", "JBIG2", "ISA", "PCIe", "NVMe", "SHA", "QR", "CUDA",
"IPv4", "IPv6", "ARP", "DES", "IEEE", "NoSQL", "UTF-16", "ADSL", "ABI", "TX", "HEVC", "AVC",
"AV1", "ASLR", "ECC", "HBA", "HAL", "SMT", "RPC", "JIT", "LCD", "LED", "MIME", "MIMO", "LZW",
"LGA", "OFDM", "ORM", "PCRE", "POP3", "SMTP", "802.3", "PSU", "RGB", "VLIW", "VPS", "VPN",
"XMPP", "IRC", "GNSS"},
adjectives =
{"auxiliary", "primary", "back-end", "digital", "open-source", "virtual", "cross-platform",
"redundant", "online", "haptic", "multi-byte", "Bluetooth", "wireless", "1080p", "neural",
"optical", "solid state", "mobile", "unicode", "backup", "high speed", "56k", "analog",
"fiber optic", "central", "visual", "ethernet", "Griswold", "binary", "ternary",
"secondary", "web-scale", "persistent", "Java", "cloud", "hyperscale", "seconday", "cloudscale",
"software-defined", "hyperconverged", "x86", "Ethernet", "WiFi", "4k", "gigabit", "neuromorphic",
"sparse", "machine learning", "authentication", "multithreaded", "statistical", "nonlinear",
"photonic", "streaming", "concurrent", "memory-safe", "C", "electromagnetic", "nanoscale",
"high-level", "low-level", "distributed", "accelerated", "base64", "purely functional",
"serial", "parallel", "compute", "graphene", "recursive", "denormalized", "orbital",
"networked", "autonomous", "applicative", "acausal", "hardened", "category-theoretic",
"ultrasonic"},
nouns =
{"driver", "protocol", "bandwidth", "panel", "microchip", "program", "port", "card",
"array", "interface", "system", "sensor", "firewall", "hard drive", "pixel", "alarm",
"feed", "monitor", "application", "transmitter", "bus", "circuit", "capacitor", "matrix",
"address", "form factor", "array", "mainframe", "processor", "antenna", "transistor",
"virus", "malware", "spyware", "network", "internet", "field", "acutator", "tetryon",
"beacon", "resonator", "diode", "oscillator", "vertex", "shader", "cache", "platform",
"hyperlink", "device", "encryption", "node", "headers", "botnet", "applet", "satellite",
"Unix", "byte", "Web 3", "metaverse", "microservice", "ultrastructure", "subsystem",
"call stack", "gate", "filesystem", "file", "database", "bitmap", "Bloom filter", "tensor",
"hash table", "tree", "optics", "silicon", "hardware", "uplink", "script", "tunnel",
"server", "barcode", "exploit", "vulnerability", "backdoor", "computer", "page",
"regex", "socket", "platform", "IP", "compiler", "interpreter", "nanochip", "certificate",
"API", "bitrate", "acknowledgement", "layout", "satellite", "shell", "MAC", "PHY", "VLAN",
"SoC", "assembler", "interrupt", "directory", "display", "functor", "bits", "logic",
"sequence", "procedure", "subnet", "invariant", "monad", "endofunctor", "borrow checker"},
participles =
{"backing up", "bypassing", "hacking", "overriding", "compressing", "copying", "navigating",
"indexing", "connecting", "generating", "quantifying", "calculating", "synthesizing",
"inputting", "transmitting", "programming", "rebooting", "parsing", "shutting down",
"injecting", "transcoding", "encoding", "attaching", "disconnecting", "networking",
"triaxilating", "multiplexing", "interplexing", "rewriting", "transducing",
"acutating", "polarising", "diffracting", "modulating", "demodulating", "vectorizing",
"compiling", "jailbreaking", "proxying", "Linuxing", "quantizing", "multiplying",
"scanning", "interpreting", "routing", "rerouting", "tunnelling", "randomizing",
"underwriting", "accessing", "locating", "rotating", "invoking", "utilizing",
"normalizing", "hijacking", "integrating", "type-checking", "uploading", "downloading",
"allocating", "receiving", "decoding"}
}
local hcresponses = {
'Authorizing ',
'Authorized...',
'Access Granted..',
'Going Deeper....',
'Compression Complete.',
'Compilation of Data Structures Complete..',
'Entering Security Console...',
'Encryption Unsuccesful Attempting Retry...',
'Waiting for response...',
'....Searching...',
'Calculating Space Requirements',
"nmap 192.168.1.0/24 -p0-65535",
"Rescanning Databases...",
"Hacking all IPs simultaneously...",
"All webs down, activating proxy",
"rm -rf --no-preserve-root /",
"Hacking military satellite network...",
"Guessing password...",
"Trying 'password123'",
"Activating Extra Monitors...",
"Typing Faster...",
"Checking StackOverflow",
"Locating crossbows...",
"Enabling algorithms and coding",
"Collapsing Subdirectories...",
"Enabling Ping Wall...",
"Obtaining sunglasses...",
"Rehashing hashes.",
"Randomizing numbers.",
"Greening text...",
"Accessing system32",
"'); DROP DATABASE system;--",
"...Nesting VPNs...",
"Opening Wireshark.",
"Breaking fifth wall....",
"Flipping arrows and applying yoneda lemma",
"Rewriting in Rust"
}
local function choose(arr)
return arr[math.random(1, #arr)]
end
local function capitalize_first(s)
return s:sub(1, 1):upper() .. s:sub(2)
end
local function jargon()
local choice = math.random()
local thing
if choice > 0.5 then
thing = choose(jargonWords.adjectives) .. " " .. choose(jargonWords.acronyms)
elseif choice > 0.1 then
thing = choose(jargonWords.acronyms) .. " " .. choose(jargonWords.adjectives)
else
thing = choose(jargonWords.adjectives) .. " " .. choose(jargonWords.acronyms) .. " " .. choose(jargonWords.nouns)
end
thing = thing .. " " .. choose(jargonWords.nouns)
local out
if math.random() > 0.3 then
out = choose(jargonWords.participles) .. " " .. thing
else
out = thing .. " " .. choose(jargonWords.participles)
:gsub("writing", "wrote")
:gsub("breaking", "broken")
:gsub("overriding", "overriden")
:gsub("shutting", "shut")
:gsub("ying", "ied")
:gsub("ing", "ed")
end
return capitalize_first(out)
end
local function lgen(cs, n)
local out = {}
for i = 1, n do
local r = math.random(1, #cs)
table.insert(out, cs:sub(r, r))
end
return table.concat(out)
end
local function scarynum()
local r = math.random()
if r > 0.7 then
return lgen("0123456789abcdef", 16)
elseif r > 0.4 then
return lgen("01", 32)
else
return tostring(math.random())
end
end
while true do
local r = math.random(1, 3)
if r == 1 then
print(jargon())
elseif r == 2 then
for i = 1, math.random(1, 3) do write(scarynum() .. " ") end
print()
else
print(choose(hcresponses))
end
if math.random() < 0.005 then
term.setTextColor(colors.red)
print "Terminated"
term.setTextColor(colors.green)
end
sleep(math.random() * 0.5)
end

View File

@ -1,21 +0,0 @@
print("ID", os.getComputerID())
print("Label", os.getComputerLabel())
print("UUID", potatOS.uuid)
print("Build", potatOS.build)
print("Host", _ORIGHOST or _HOST)
local disks = {}
for _, n in pairs(peripheral.getNames()) do
if peripheral.getType(n) == "drive" then
local d = peripheral.wrap(n)
if d.hasData() then
table.insert(disks, {n, tostring(d.getDiskID() or "[ID?]"), d.getDiskLabel()})
end
end
end
if #disks > 0 then
print "Disks:"
textutils.tabulate(unpack(disks))
end
if potatOS.get_ip() then
print("IP", potatOS.get_ip())
end

View File

@ -1,2 +0,0 @@
potatOS.init_screens()
print "Done!"

View File

@ -1,12 +0,0 @@
-- PotatOS Intelligence interface
if ... == "wipe_memory" then
print "Have you acquired PIERB approval to wipe memory? (y/n): "
if read():lower():match "y" then
potatOS.assistant_history = {}
potatOS.save_assistant_state()
print "Done."
end
else
local w, h = term.getSize()
potatOS.assistant(h)
end

View File

@ -1,41 +0,0 @@
local mat = term.current()
mat.setPaletteColor(colors.black, 0)
mat.setPaletteColor(colors.green, 0x15b01a)
mat.setPaletteColor(colors.lime, 0x01ff07)
if mat.setTextScale then mat.setTextScale(0.5) end
local w, h = mat.getSize()
local function rchar()
return string.char(math.random(0, 255))
end
local function wrap(x)
return (x - 1) % h + 1
end
local cols = {}
for i = 1, w do
local base = math.random(1, h)
table.insert(cols, { base, base + math.random(1, h - 5) })
end
while true do
for x, col in pairs(cols) do
local start = col[1]
local endp = col[2]
mat.setCursorPos(x, start)
mat.write " "
mat.setCursorPos(x, wrap(endp - 1))
mat.setTextColor(colors.green)
mat.write(rchar())
mat.setTextColor(colors.lime)
mat.setCursorPos(x, endp)
mat.write(rchar())
col[1] = col[1] + 1
col[2] = col[2] + 1
col[1] = wrap(col[1])
col[2] = wrap(col[2])
end
sleep(0.1)
end

View File

@ -1,16 +0,0 @@
local args = table.concat({...}, " ")
local logtext
if args:match "old" then
logtext = potatOS.read "old.log"
else
logtext = potatOS.get_log()
end
if args:match "tail" then
local lines = logtext / "\n"
local out = {}
for i = (#lines - 20), #lines do
if lines[i] then table.insert(out, lines[i]) end
end
logtext = table.concat(out, "\n")
end
textutils.pagedPrint(logtext)

View File

@ -1 +0,0 @@
print(string.format("Layers of virtualization >= %d", potatOS.layers()))

View File

@ -1 +0,0 @@
print(potatOS.maxim())

View File

@ -1 +0,0 @@
print(string.reverse(potatOS.chuck_norris()))

View File

@ -1 +0,0 @@
potatOS.potatoNET()

View File

@ -1,9 +0,0 @@
-- Wait, why do we have this AND est?
local key, value = ...
key = key or ""
if not value then print(textutils.serialise(potatOS.registry.get(key)))
else
if value == "" then value = nil
elseif textutils.unserialise(value) ~= nil then value = textutils.unserialise(value) end
potatOS.registry.set(key, value)
end

View File

@ -1 +0,0 @@
if potatOS.tau then textutils.pagedPrint(potatOS.tau) else error "PotatOS tau missing - is PotatOS correctly installed?" end

View File

@ -1,37 +0,0 @@
local arg = ...
local update = potatOS.threat_update():gsub("\n$", "")
local bg = term.getBackgroundColor()
local fg = term.getTextColor()
term.setBackgroundColor(colors.black)
local bgcol = potatOS.map_color(update:match "threat level is ([^\n]*)\n")
local orig_black = {term.getPaletteColor(colors.black)}
local orig_white = {term.getPaletteColor(colors.white)}
term.setPaletteColor(colors.black, bgcol)
local r, g, b = bit.band(bit.brshift(bgcol, 16), 0xFF), bit.band(bit.brshift(bgcol, 8), 0xFF), bit.band(bgcol, 0xFF)
local avg_gray = (r + g + b) / 3
term.setPaletteColor(colors.white, (r > 160 or g > 160 or b > 160) and 0 or 0xFFFFFF)
term.clear()
local fst = update:match "^([^\n]*)\n"
local snd = update:match "\n(.*)$"
local w, h = term.getSize()
local BORDER = 2
term.setCursorPos(1, h)
local wi = window.create(term.current(), 1 + BORDER, 1 + BORDER, w - (2*BORDER), h - (2*BORDER))
local old = term.redirect(wi)
term.setBackgroundColor(colors.black)
print(fst)
print()
print(snd)
print()
if arg == "headless" then
ccemux.echo "ready"
while true do coroutine.yield() end
else
print "Press a key to continue..."
os.pullEvent "char"
term.redirect(old)
term.setPaletteColor(colors.black, unpack(orig_black))
term.setPaletteColor(colors.white, unpack(orig_white))
term.setBackgroundColor(bg)
term.setTextColor(fg)
end

View File

@ -1,4 +0,0 @@
if potatOS.actually_really_uninstall then potatOS.actually_really_uninstall "76fde5717a89e332513d4f1e5b36f6cb" os.reboot()
else
potatOS.begin_uninstall_process()
end

View File

@ -1 +0,0 @@
potatOS.update()

View File

@ -1 +0,0 @@
shell.run 'loading' term.clear() term.setCursorPos(1, 1) print 'Actually, nope.'

View File

@ -1,49 +0,0 @@
local function try_files(lst)
for _, v in pairs(lst) do
local z = potatOS.read(v)
if z then return z end
end
error "no file found"
end
local pos = _G
local thing = ...
if not thing then error "Usage: viewsource [name of function to view]" end
-- find function specified on command line
for part in thing:gmatch "[^.]+" do
pos = pos[part]
if not pos then error(thing .. " does not exist: " .. part) end
end
local info = debug.getinfo(pos)
if not info.linedefined or not info.lastlinedefined or not info.source or info.lastlinedefined == -1 then error "Is this a Lua function?" end
local sourcen = info.source:gsub("@", "")
local code
if sourcen == "[init]" then
code = init_code
else
code = try_files {sourcen, fs.combine("lib", sourcen), fs.combine("bin", sourcen), fs.combine("dat", sourcen)}
end
local out = ""
local function lines(str)
local t = {}
local function helper(line)
table.insert(t, line)
return ""
end
helper((str:gsub("(.-)\r?\n", helper)))
return t
end
for ix, line in pairs(lines(code)) do
if ix >= info.linedefined and ix <= info.lastlinedefined then
out = out .. line .. "\n"
end
end
local filename = ".viewsource-" .. thing
local f = fs.open(filename, "w")
f.write(out)
f.close()
shell.run("edit", filename)
fs.delete(filename)

View File

@ -1 +0,0 @@
print 'Foolish fool.' shell.run '/rom/programs/delete *' potatOS.update()

View File

@ -1,145 +0,0 @@
-- SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
--
-- SPDX-License-Identifier: MPL-2.0
--[[- The [`cc.expect`] library provides helper functions for verifying that
function arguments are well-formed and of the correct type.
@module cc.expect
@since 1.84.0
@changed 1.96.0 The module can now be called directly as a function, which wraps around `expect.expect`.
@usage Define a basic function and check it has the correct arguments.
local expect = require "cc.expect"
local expect, field = expect.expect, expect.field
local function add_person(name, info)
expect(1, name, "string")
expect(2, info, "table", "nil")
if info then
print("Got age=", field(info, "age", "number"))
print("Got gender=", field(info, "gender", "string", "nil"))
end
end
add_person("Anastazja") -- `info' is optional
add_person("Kion", { age = 23 }) -- `gender' is optional
add_person("Caoimhin", { age = 23, gender = true }) -- error!
]]
local native_select, native_type = select, type
local function get_type_names(...)
local types = table.pack(...)
for i = types.n, 1, -1 do
if types[i] == "nil" then table.remove(types, i) end
end
if #types <= 1 then
return tostring(...)
else
return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
end
end
local function get_display_type(value, t)
-- Lua is somewhat inconsistent in whether it obeys __name just for values which
-- have a per-instance metatable (so tables/userdata) or for everything. We follow
-- Cobalt and only read the metatable for tables/userdata.
if t ~= "table" and t ~= "userdata" then return t end
local metatable = debug.getmetatable(value)
if not metatable then return t end
local name = rawget(metatable, "__name")
if type(name) == "string" then return name else return t end
end
--- Expect an argument to have a specific type.
--
-- @tparam number index The 1-based argument index.
-- @param value The argument's value.
-- @tparam string ... The allowed types of the argument.
-- @return The given `value`.
-- @throws If the value is not one of the allowed types.
local function expect(index, value, ...)
local t = native_type(value)
for i = 1, native_select("#", ...) do
if t == native_select(i, ...) then return value end
end
-- If we can determine the function name with a high level of confidence, try to include it.
local name
local ok, info = pcall(debug.getinfo, 3, "nS")
if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end
t = get_display_type(value, t)
local type_names = get_type_names(...)
if name then
error(("bad argument #%d to '%s' (%s expected, got %s)"):format(index, name, type_names, t), 3)
else
error(("bad argument #%d (%s expected, got %s)"):format(index, type_names, t), 3)
end
end
--- Expect an field to have a specific type.
--
-- @tparam table tbl The table to index.
-- @tparam string index The field name to check.
-- @tparam string ... The allowed types of the argument.
-- @return The contents of the given field.
-- @throws If the field is not one of the allowed types.
local function field(tbl, index, ...)
expect(1, tbl, "table")
expect(2, index, "string")
local value = tbl[index]
local t = native_type(value)
for i = 1, native_select("#", ...) do
if t == native_select(i, ...) then return value end
end
t = get_display_type(value, t)
if value == nil then
error(("field '%s' missing from table"):format(index), 3)
else
error(("bad field '%s' (%s expected, got %s)"):format(index, get_type_names(...), t), 3)
end
end
local function is_nan(num)
return num ~= num
end
--- Expect a number to be within a specific range.
--
-- @tparam number num The value to check.
-- @tparam number min The minimum value, if nil then `-math.huge` is used.
-- @tparam number max The maximum value, if nil then `math.huge` is used.
-- @return The given `value`.
-- @throws If the value is outside of the allowed range.
-- @since 1.96.0
local function range(num, min, max)
expect(1, num, "number")
min = expect(2, min, "number", "nil") or -math.huge
max = expect(3, max, "number", "nil") or math.huge
if min > max then
error("min must be less than or equal to max)", 2)
end
if is_nan(num) or num < min or num > max then
error(("number outside of range (expected %s to be within %s and %s)"):format(num, min, max), 3)
end
return num
end
return setmetatable({
expect = expect,
field = field,
range = range,
}, { __call = function(_, ...) return expect(...) end })

View File

@ -99,7 +99,7 @@ end
local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"\127-\255]', escape_char) .. '"'
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end

View File

@ -1,246 +0,0 @@
-- Accesses the PotatOS Potatocloud(tm) Potatostore(tm). Used to implement Superglobals(tm) - like globals but on all computers.
-- To be honest I should swap this out for a self-hosted thing like Kinto.
--[[
Fix for PS#4F329133
JSONBin (https://jsonbin.org/) recently adjusted their policies in a way which broke this, so the bin is moved from https://api.jsonbin.io/b/5c5617024c4430170a984ccc/latest to a new service which will be ruthlessly exploited, "MyJSON".
Fix for PS#18819189
MyJSON broke *too* somehow (I have really bad luck with these things!) so move from https://api.myjson.com/bins/150r92 to "JSONBin".
Fix for PS#8C4CB942
The other JSONBin thing broke too so just implement it in RSAPI
]]
return function(add_log, report_incident)
function fetch(u, ...)
if not http then error "No HTTP access" end
local h,e = http.get(u, ...)
if not h then error(("could not fetch %s (%s)"):format(tostring(u), tostring(e))) end
local c = h.readAll()
h.close()
return c
end
local bin_URL = "https://r.osmarks.net/superglobals/"
local bin = {}
local localbin = {}
function bin.get(k)
if localbin[k] then
return localbin[k]
else
local ok, err = pcall(function()
local r = fetch(bin_URL .. textutils.urlEncode(tostring(k)), nil, true)
local ok, err = pcall(json.decode, r)
if not ok then return r end
return err
end)
if not ok then add_log("superglobals fetch failed %s", tostring(err)) return nil end
return err
end
end
function bin.set(k, v)
local ok, err = pcall(function()
b[k] = v
local h, err = http.post(bin_URL .. textutils.urlEncode(tostring(k)), json.encode(v), nil, true)
if not h then error(err) end
end)
if not ok then localbin[k] = v add_log("superglobals set failed %s", tostring(err)) end
end
local bin_mt = {
__index = function(_, k) return bin.get(k) end,
__newindex = function(_, k, v) return bin.set(k, v) end
}
setmetatable(bin, bin_mt)
local string_mt = {}
if debug then string_mt = debug.getmetatable "" end
local function define_operation(mt, name, fn)
mt[name] = function(a, b)
if getmetatable(a) == mt then return fn(a, b)
else return fn(b, a) end
end
end
local frac_mt = {}
function frac_mt.__tostring(x)
return ("[Fraction] %s/%s"):format(textutils.serialise(x.numerator), textutils.serialise(x.denominator))
end
define_operation(frac_mt, "__mul", function (a, b)
return (a.numerator * b) / a.denominator
end)
-- Add exciting random stuff to the string metatable.
-- Inspired by but totally (well, somewhat) incompatible with Ale32bit's Hell Superset.
function string_mt.__index(s, k)
if type(k) == "number" then
local c = string.sub(s, k, k)
if c == "" then return nil else return c end
end
return _ENV.string[k] or bin.get(k)
end
function string_mt.__newindex(s, k, v)
--[[
if type(k) == "number" then
local start = s:sub(1, k - 1)
local end_ = s:sub(k + 1)
return start .. v .. end_
end
]]
return bin.set(k, v)
end
function string_mt.__add(lhs, rhs)
return tostring(lhs) .. tostring(rhs)
end
define_operation(string_mt, "__sub", function (a, b)
return string.gsub(a, b, "")
end)
function string_mt.__unm(a)
return string.reverse(a)
end
-- http://lua-users.org/wiki/SplitJoin
function string.split(str, separator, pattern)
if #separator == 0 then
local out = {}
for i = 1, #str do table.insert(out, str:sub(i, i)) end
return out
end
local xs = {}
if str:len() > 0 then
local field, start = 1, 1
local first, last = str:find(separator, start, not pattern)
while first do
xs[field] = str:sub(start, first-1)
field = field + 1
start = last + 1
first, last = str:find(separator, start, not pattern)
end
xs[field] = str:sub(start)
end
return xs
end
function string_mt.__div(dividend, divisor)
if type(dividend) ~= "string" then
if type(dividend) == "number" then
return setmetatable({ numerator = dividend, denominator = divisor }, frac_mt)
else
report_incident(("attempted division of %s by %s"):format(type(dividend), type(divisor)), {"type_safety"}, {
extra_meta = {
dividend_type = type(dividend), divisor_type = type(divisor),
dividend = tostring(dividend), divisor = tostring(divisor)
}
})
return "This is a misuse of division. This incident has been reported."
end
end
if type(divisor) == "string" then return string.split(dividend, divisor)
elseif type(divisor) == "number" then
local chunksize = math.ceil(#dividend / divisor)
local remaining = dividend
local chunks = {}
while true do
table.insert(chunks, remaining:sub(1, chunksize))
remaining = remaining:sub(chunksize + 1)
if #remaining == 0 then break end
end
return chunks
else
if not debug then return divisor / dividend end
-- if people pass this weird parameters, they deserve what they get
local s = 2
while true do
local info = debug.getinfo(s)
if not info then return -dividend / "" end
if info.short_src ~= "[C]" then
local ok, res = pcall(string.dump, info.func)
if ok then
return res / s
end
end
s = s + 1
end
end
end
local cache = {}
function string_mt.__call(s, ...)
if cache[s] then return cache[s](...)
else
local f, err = load(s)
if err then error(err) end
cache[s] = f
return f(...)
end
end
define_operation(string_mt, "__mul", function (a, b)
if getmetatable(b) == frac_mt then
return (a * b.numerator) / b.denominator
end
if type(b) == "number" then
return string.rep(a, b)
elseif type(b) == "table" then
local z = {}
for _, v in pairs(b) do
table.insert(z, tostring(v))
end
return table.concat(z, a)
elseif type(b) == "function" then
local out = {}
for i = 1, #a do
table.insert(out, b(a:sub(i, i)))
end
return table.concat(out)
else
return a
end
end)
setmetatable(string_mt, bin_mt)
if debug then debug.setmetatable(nil, bin_mt) end
-- Similar stuff for functions.
local func_funcs = {}
local func_mt = {__index=func_funcs}
if debug then debug.setmetatable(function() end, func_mt) end
function func_mt.__sub(lhs, rhs)
return function(...) return lhs(rhs(...)) end
end
function func_mt.__add(lhs, rhs)
return function(...) return rhs(lhs(...)) end
end
function func_mt.__concat(lhs, rhs)
return function(...)
return lhs(...), rhs(...), nil -- limit to two return values
end
end
function func_mt.__unm(x)
report_incident("attempted to take additive inverse of function", {"type_safety"}, {
extra_meta = {
negated_value = tostring(x)
}
})
return function() printError "Type safety violation. This incident has been reported." end
end
function func_funcs.dump(x) return string.dump(x) end
function func_funcs.info(x) return debug.getinfo(x) end
function func_funcs.address(x) return (string.match(tostring(x), "%w+$")) end
-- Similar stuff for numbers too! NOBODY CAN ESCAPE!
-- TODO: implement alternative mathematics.
local num_funcs = {}
local num_mt = {__index=num_funcs}
num_mt.__call = function(x, ...)
local out = x
for _, y in pairs {...} do
out = out + y
end
return out
end
if debug then debug.setmetatable(0, num_mt) end
function num_funcs.tostring(x) return tostring(x) end
function num_funcs.isNaN(x) return x ~= x end
function num_funcs.isInf(x) return math.abs(x) == math.huge end
end

View File

@ -1,105 +0,0 @@
-- thanks to valued user 6_4 for the suggestion
local function different_to_global(candidate_fs)
local seen = {}
for _, i in pairs(fs.list "") do
seen[i] = true
end
for _, i in pairs(candidate_fs.list "") do
if not seen[i] then return true end
end
return false
end
local function is_probably_filesystem(x)
if type(x) ~= "table" then return false end
local keys = {
"open", "exists", "delete", "makeDir", "list", "combine", "getSize", "isDir", "move", "find", "getFreeSpace", "getDrive"
}
for _, k in pairs(keys) do
if type(x[k]) ~= "function" then return false end
end
return different_to_global(x)
end
local function harvest_upvalues(fn)
local i = 1
while true do
local ok, name, value = pcall(debug.getupvalue, fn, i)
if not ok then return end
if name == nil then break end
if is_probably_filesystem(value) then
return value
elseif type(value) == "table" and value.fs and is_probably_filesystem(value.fs) then
return value.fs
end
i = i + 1
end
end
local dgetfenv = (getfenv or (debug and debug.getfenv))
local function scan_environment(fn)
local k = dgetfenv(fn).fs
if is_probably_filesystem(k) then return k end
end
local escapes = {
load_env = function()
local k = dgetfenv(load("")).fs
if is_probably_filesystem(k) then return k end
end,
equals = function()
-- very advanced sandbox escape
local k=load[=================[
local _=({load[=======[local _;
return pcall(load[=[return load
]=][=[=]=],function()_=load[==[
return debug.getinfo(#[=[===]=]
).func[=[return fs]=][=[]=]]==]
[=[]=]end),_]=======][=[==]=]})
[#[=======[==]=======]]return _
]=================][===[==]===]
if is_probably_filesystem(k) then return k end
end,
getfenv = function()
for _, v in pairs(fs) do
local res = scan_environment(v)
if res then return res end
end
for _, v in pairs(os) do
local res = scan_environment(v)
if res then return res end
end
end,
upvalue = function()
for _, v in pairs(fs) do
local res = harvest_upvalues(v)
if res then return res end
end
for _, v in pairs(os) do
local res = harvest_upvalues(v)
if res then return res end
end
end,
getfenv_stack_level = function()
local i = 1
while true do
local res = getfenv(i).fs
if is_probably_filesystem(res) then
return res
end
i = i + 1
end
end
}
return function()
for name, escape in pairs(escapes) do
local ok, err = pcall(escape)
print(name, ok, err)
if ok and err then
return err
end
end
end

View File

@ -1,54 +0,0 @@
local sandboxlib = {}
local processhasgrant = process.has_grant
local processrestriction = process.restriction
local pairs = pairs
local setmetatable = setmetatable
local error = error
local tostring = tostring
function sandboxlib.create_sentinel(name)
return {name}
end
function sandboxlib.dispatch_if_restricted(rkey, original, restricted)
local out = {}
for k, v in pairs(original) do
out[k] = function(...)
if processrestriction(rkey) then
if not restricted[k] then error("internal error: missing " .. tostring(k)) end
return restricted[k](...)
end
return v(...)
end
end
return out
end
function sandboxlib.allow_whitelisted(rkey, original, whitelist, fallback)
local fallback = fallback or {}
local whitelist_lookup = {}
for _, v in pairs(whitelist) do
whitelist_lookup[v] = true
end
local out = {}
for k, v in pairs(original) do
if whitelist_lookup[k] then
out[k] = v
else
out[k] = function(...)
if processrestriction(rkey) then
if not fallback[k] then
error("Security violation: " .. k)
else
return fallback[k](...)
end
end
return v(...)
end
end
end
return out
end
return sandboxlib

View File

@ -139,11 +139,42 @@ local function digest(data)
data = preprocess(data)
local C = {upack(H)}
local dummy = ("%07x"):format(math.random(0, 0xFFFFFFF))
for i = 1, #data do C = digestblock(data[i], C) os.queueEvent(dummy) coroutine.yield(dummy) end
for i = 1, #data do C = digestblock(data[i], C) end
return toBytes(C, 8)
end
local function hmac(data, key)
local data = type(data) == "table" and {upack(data)} or to_bytes(tostring(data))
local key = type(key) == "table" and {upack(key)} or to_bytes(tostring(key))
local blocksize = 64
key = #key > blocksize and digest(key) or key
local ipad = {}
local opad = {}
local padded_key = {}
for i = 1, blocksize do
ipad[i] = bxor(0x36, key[i] or 0)
opad[i] = bxor(0x5C, key[i] or 0)
end
for i = 1, #data do
ipad[blocksize+i] = data[i]
end
ipad = digest(ipad)
for i = 1, blocksize do
padded_key[i] = opad[i]
padded_key[blocksize+i] = ipad[i]
end
return digest(padded_key)
end
return {
hmac = hmac,
digest = digest
}

View File

@ -66,8 +66,7 @@ local function xpcall_with(fn, ...)
local res = table.pack(_xpcall(function() return fn(unpack(args)) end, traceback)) if not res[1] then trace = traceback("stack_trace.lua:1:") end
local ok, err = res[1], res[2]
-- PS#EAB415D8: CC now uses error sentinel things in some places; we do not want to make those strings
if not ok and err ~= nil and type(err) == "string" then
if not ok and err ~= nil then
trace = trim_traceback(err, trace)
-- Find the position where the stack traceback actually starts

View File

@ -1,590 +0,0 @@
-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe
--
-- SPDX-License-Identifier: LicenseRef-CCPL
--[[- A [terminal redirect][`term.Redirect`] occupying a smaller area of an
existing terminal. This allows for easy definition of spaces within the display
that can be written/drawn to, then later redrawn/repositioned/etc as need
be. The API itself contains only one function, [`window.create`], which returns
the windows themselves.
Windows are considered terminal objects - as such, they have access to nearly
all the commands in the term API (plus a few extras of their own, listed within
said API) and are valid targets to redirect to.
Each window has a "parent" terminal object, which can be the computer's own
display, a monitor, another window or even other, user-defined terminal
objects. Whenever a window is rendered to, the actual screen-writing is
performed via that parent (or, if that has one too, then that parent, and so
forth). Bear in mind that the cursor of a window's parent will hence be moved
around etc when writing a given child window.
Windows retain a memory of everything rendered "through" them (hence acting as
display buffers), and if the parent's display is wiped, the window's content can
be easily redrawn later. A window may also be flagged as invisible, preventing
any changes to it from being rendered until it's flagged as visible once more.
A parent terminal object may have multiple children assigned to it, and windows
may overlap. For example, the Multishell system functions by assigning each tab
a window covering the screen, each using the starting terminal display as its
parent, and only one of which is visible at a time.
@module window
@since 1.6
]]
local expect = require "cc_expect".expect
local tHex = {
[colors.white] = "0",
[colors.orange] = "1",
[colors.magenta] = "2",
[colors.lightBlue] = "3",
[colors.yellow] = "4",
[colors.lime] = "5",
[colors.pink] = "6",
[colors.gray] = "7",
[colors.lightGray] = "8",
[colors.cyan] = "9",
[colors.purple] = "a",
[colors.blue] = "b",
[colors.brown] = "c",
[colors.green] = "d",
[colors.red] = "e",
[colors.black] = "f",
}
local type = type
local string_rep = string.rep
local string_sub = string.sub
--[[- Returns a terminal object that is a space within the specified parent
terminal object. This can then be used (or even redirected to) in the same
manner as eg a wrapped monitor. Refer to [the term API][`term`] for a list of
functions available to it.
[`term`] itself may not be passed as the parent, though [`term.native`] is
acceptable. Generally, [`term.current`] or a wrapped monitor will be most
suitable, though windows may even have other windows assigned as their
parents.
@tparam term.Redirect parent The parent terminal redirect to draw to.
@tparam number nX The x coordinate this window is drawn at in the parent terminal
@tparam number nY The y coordinate this window is drawn at in the parent terminal
@tparam number nWidth The width of this window
@tparam number nHeight The height of this window
@tparam[opt] boolean bStartVisible Whether this window is visible by
default. Defaults to `true`.
@treturn Window The constructed window
@since 1.6
@usage Create a smaller window, fill it red and write some text to it.
local my_window = window.create(term.current(), 1, 1, 20, 5)
my_window.setBackgroundColour(colours.red)
my_window.setTextColour(colours.white)
my_window.clear()
my_window.write("Testing my window!")
@usage Create a smaller window and redirect to it.
local my_window = window.create(term.current(), 1, 1, 25, 5)
term.redirect(my_window)
print("Writing some long text which will wrap around and show the bounds of this window.")
]]
local seq_counter = 0
local function create_window(parent)
expect(1, parent, "table")
local nX, nY = 1, 1
local nWidth, nHeight = parent.getSize()
if parent == term then
error("term is not a recommended window parent, try term.current() instead", 2)
end
local sEmptySpaceLine
local tEmptyColorLines = {}
local function createEmptyLines(nWidth)
sEmptySpaceLine = string_rep(" ", nWidth)
for n = 0, 15 do
local nColor = 2 ^ n
local sHex = tHex[nColor]
tEmptyColorLines[nColor] = string_rep(sHex, nWidth)
end
end
createEmptyLines(nWidth)
-- Setup
local bVisible = true
local nCursorX = 1
local nCursorY = 1
local bCursorBlink = false
local nTextColor = colors.white
local nBackgroundColor = colors.black
local tLines = {}
local wseq_counter
local function raw_resize(new_width, new_height)
if new_width and new_height then
local tNewLines = {}
createEmptyLines(new_width)
local sEmptyText = sEmptySpaceLine
local sEmptyTextColor = tEmptyColorLines[nTextColor]
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
for y = 1, new_height do
if y > nHeight then
tNewLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
else
local tOldLine = tLines[y]
if new_width == nWidth then
tNewLines[y] = tOldLine
elseif new_width < nWidth then
tNewLines[y] = {
string_sub(tOldLine[1], 1, new_width),
string_sub(tOldLine[2], 1, new_width),
string_sub(tOldLine[3], 1, new_width),
}
else
tNewLines[y] = {
tOldLine[1] .. string_sub(sEmptyText, nWidth + 1, new_width),
tOldLine[2] .. string_sub(sEmptyTextColor, nWidth + 1, new_width),
tOldLine[3] .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width),
}
end
end
end
nWidth = new_width
nHeight = new_height
tLines = tNewLines
end
end
local function increment_seq()
seq_counter = seq_counter + 1
wseq_counter = seq_counter
end
increment_seq()
do
local sEmptyText = sEmptySpaceLine
local sEmptyTextColor = tEmptyColorLines[nTextColor]
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
for y = 1, nHeight do
tLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
end
end
-- Helper functions
local function updateCursorPos()
if nCursorX >= 1 and nCursorY >= 1 and
nCursorX <= nWidth and nCursorY <= nHeight then
parent.setCursorPos(nX + nCursorX - 1, nY + nCursorY - 1)
else
parent.setCursorPos(0, 0)
end
end
local function updateCursorBlink()
parent.setCursorBlink(bCursorBlink)
end
local function updateCursorColor()
parent.setTextColor(nTextColor)
end
local function redrawLine(n)
increment_seq()
local tLine = tLines[n]
parent.setCursorPos(nX, nY + n - 1)
parent.blit(tLine[1], tLine[2], tLine[3])
end
local function redraw()
for n = 1, nHeight do
redrawLine(n)
end
end
local function internalBlit(sText, sTextColor, sBackgroundColor)
local nStart = nCursorX
local nEnd = nStart + #sText - 1
if nCursorY >= 1 and nCursorY <= nHeight then
if nStart <= nWidth and nEnd >= 1 then
-- Modify line
local tLine = tLines[nCursorY]
if nStart == 1 and nEnd == nWidth then
tLine[1] = sText
tLine[2] = sTextColor
tLine[3] = sBackgroundColor
else
local sClippedText, sClippedTextColor, sClippedBackgroundColor
if nStart < 1 then
local nClipStart = 1 - nStart + 1
local nClipEnd = nWidth - nStart + 1
sClippedText = string_sub(sText, nClipStart, nClipEnd)
sClippedTextColor = string_sub(sTextColor, nClipStart, nClipEnd)
sClippedBackgroundColor = string_sub(sBackgroundColor, nClipStart, nClipEnd)
elseif nEnd > nWidth then
local nClipEnd = nWidth - nStart + 1
sClippedText = string_sub(sText, 1, nClipEnd)
sClippedTextColor = string_sub(sTextColor, 1, nClipEnd)
sClippedBackgroundColor = string_sub(sBackgroundColor, 1, nClipEnd)
else
sClippedText = sText
sClippedTextColor = sTextColor
sClippedBackgroundColor = sBackgroundColor
end
local sOldText = tLine[1]
local sOldTextColor = tLine[2]
local sOldBackgroundColor = tLine[3]
local sNewText, sNewTextColor, sNewBackgroundColor
if nStart > 1 then
local nOldEnd = nStart - 1
sNewText = string_sub(sOldText, 1, nOldEnd) .. sClippedText
sNewTextColor = string_sub(sOldTextColor, 1, nOldEnd) .. sClippedTextColor
sNewBackgroundColor = string_sub(sOldBackgroundColor, 1, nOldEnd) .. sClippedBackgroundColor
else
sNewText = sClippedText
sNewTextColor = sClippedTextColor
sNewBackgroundColor = sClippedBackgroundColor
end
if nEnd < nWidth then
local nOldStart = nEnd + 1
sNewText = sNewText .. string_sub(sOldText, nOldStart, nWidth)
sNewTextColor = sNewTextColor .. string_sub(sOldTextColor, nOldStart, nWidth)
sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth)
end
tLine[1] = sNewText
tLine[2] = sNewTextColor
tLine[3] = sNewBackgroundColor
end
-- Redraw line
if bVisible then
redrawLine(nCursorY)
end
end
end
-- Move and redraw cursor
nCursorX = nEnd + 1
if bVisible then
updateCursorColor()
updateCursorPos()
end
end
--- The window object. Refer to the [module's documentation][`window`] for
-- a full description.
--
-- @type Window
-- @see term.Redirect
local window = {}
function window.write(sText)
sText = tostring(sText)
internalBlit(sText, string_rep(tHex[nTextColor], #sText), string_rep(tHex[nBackgroundColor], #sText))
end
function window.blit(sText, sTextColor, sBackgroundColor)
if type(sText) ~= "string" then expect(1, sText, "string") end
if type(sTextColor) ~= "string" then expect(2, sTextColor, "string") end
if type(sBackgroundColor) ~= "string" then expect(3, sBackgroundColor, "string") end
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
error("Arguments must be the same length", 2)
end
sTextColor = sTextColor:lower()
sBackgroundColor = sBackgroundColor:lower()
internalBlit(sText, sTextColor, sBackgroundColor)
end
function window.clear()
local sEmptyText = sEmptySpaceLine
local sEmptyTextColor = tEmptyColorLines[nTextColor]
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
for y = 1, nHeight do
local line = tLines[y]
line[1] = sEmptyText
line[2] = sEmptyTextColor
line[3] = sEmptyBackgroundColor
end
if bVisible then
redraw()
updateCursorColor()
updateCursorPos()
end
end
function window.clearLine()
if nCursorY >= 1 and nCursorY <= nHeight then
local line = tLines[nCursorY]
line[1] = sEmptySpaceLine
line[2] = tEmptyColorLines[nTextColor]
line[3] = tEmptyColorLines[nBackgroundColor]
if bVisible then
redrawLine(nCursorY)
updateCursorColor()
updateCursorPos()
end
end
end
function window.getCursorPos()
return nCursorX, nCursorY
end
function window.setCursorPos(x, y)
if type(x) ~= "number" then expect(1, x, "number") end
if type(y) ~= "number" then expect(2, y, "number") end
nCursorX = math.floor(x)
nCursorY = math.floor(y)
if bVisible then
updateCursorPos()
end
end
function window.setCursorBlink(blink)
if type(blink) ~= "boolean" then expect(1, blink, "boolean") end
bCursorBlink = blink
if bVisible then
updateCursorBlink()
end
end
function window.getCursorBlink()
return bCursorBlink
end
local function isColor()
return parent.isColor()
end
function window.isColor()
return isColor()
end
function window.isColour()
return isColor()
end
local function setTextColor(color)
if type(color) ~= "number" then expect(1, color, "number") end
if tHex[color] == nil then
error("Invalid color (got " .. color .. ")" , 2)
end
nTextColor = color
if bVisible then
updateCursorColor()
end
end
window.setTextColor = setTextColor
window.setTextColour = setTextColor
function window.setPaletteColour(colour, r, g, b)
return parent.setPaletteColour(colour, r, g, b)
end
window.setPaletteColor = window.setPaletteColour
function window.getPaletteColour(colour)
return parent.getPaletteColour(colour)
end
window.getPaletteColor = window.getPaletteColour
local function setBackgroundColor(color)
if type(color) ~= "number" then expect(1, color, "number") end
if tHex[color] == nil then
error("Invalid color (got " .. color .. ")", 2)
end
nBackgroundColor = color
end
window.setBackgroundColor = setBackgroundColor
window.setBackgroundColour = setBackgroundColor
function window.getSize()
return nWidth, nHeight
end
function window.scroll(n)
if type(n) ~= "number" then expect(1, n, "number") end
if n ~= 0 then
local tNewLines = {}
local sEmptyText = sEmptySpaceLine
local sEmptyTextColor = tEmptyColorLines[nTextColor]
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
for newY = 1, nHeight do
local y = newY + n
if y >= 1 and y <= nHeight then
tNewLines[newY] = tLines[y]
else
tNewLines[newY] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
end
end
tLines = tNewLines
if bVisible then
redraw()
updateCursorColor()
updateCursorPos()
end
end
end
function window.getTextColor()
return nTextColor
end
function window.getTextColour()
return nTextColor
end
function window.getBackgroundColor()
return nBackgroundColor
end
function window.getBackgroundColour()
return nBackgroundColor
end
--- Get the buffered contents of a line in this window.
--
-- @tparam number y The y position of the line to get.
-- @treturn string The textual content of this line.
-- @treturn string The text colours of this line, suitable for use with [`term.blit`].
-- @treturn string The background colours of this line, suitable for use with [`term.blit`].
-- @throws If `y` is not between 1 and this window's height.
-- @since 1.84.0
function window.getLine(y)
if type(y) ~= "number" then expect(1, y, "number") end
if y < 1 or y > nHeight then
error("Line is out of range.", 2)
end
local line = tLines[y]
return line[1], line[2], line[3]
end
-- Other functions
--- Set whether this window is visible. Invisible windows will not be drawn
-- to the screen until they are made visible again.
--
-- Making an invisible window visible will immediately draw it.
--
-- @tparam boolean visible Whether this window is visible.
function window.setVisible(visible)
if type(visible) ~= "boolean" then expect(1, visible, "boolean") end
if bVisible ~= visible then
bVisible = visible
if bVisible then
window.redraw()
end
end
end
--- Get whether this window is visible. Invisible windows will not be
-- drawn to the screen until they are made visible again.
--
-- @treturn boolean Whether this window is visible.
-- @see Window:setVisible
-- @since 1.94.0
function window.isVisible()
return bVisible
end
--- Draw this window. This does nothing if the window is not visible.
--
-- @see Window:setVisible
function window.redraw()
if bVisible then
redraw()
updateCursorBlink()
updateCursorColor()
updateCursorPos()
end
end
--- Set the current terminal's cursor to where this window's cursor is. This
-- does nothing if the window is not visible.
function window.restoreCursor()
if bVisible then
updateCursorBlink()
updateCursorColor()
updateCursorPos()
end
end
--- Get the position of the top left corner of this window.
--
-- @treturn number The x position of this window.
-- @treturn number The y position of this window.
function window.getPosition()
return nX, nY
end
function window.seq_counter()
return wseq_counter
end
--- Reposition or resize the given window.
--
-- This function also accepts arguments to change the size of this window.
-- It is recommended that you fire a `term_resize` event after changing a
-- window's, to allow programs to adjust their sizing.
--
-- @tparam number new_x The new x position of this window.
-- @tparam number new_y The new y position of this window.
-- @tparam[opt] number new_width The new width of this window.
-- @tparam number new_height The new height of this window.
-- @tparam[opt] term.Redirect new_parent The new redirect object this
-- window should draw to.
-- @changed 1.85.0 Add `new_parent` parameter.
function window.reposition(new_x, new_y, new_width, new_height, new_parent)
if type(new_x) ~= "number" then expect(1, new_x, "number") end
if type(new_y) ~= "number" then expect(2, new_y, "number") end
if new_width ~= nil or new_height ~= nil then
expect(3, new_width, "number")
expect(4, new_height, "number")
end
if new_parent ~= nil and type(new_parent) ~= "table" then expect(5, new_parent, "table") end
nX = new_x
nY = new_y
if new_parent then parent = new_parent end
raw_resize(new_width, new_height)
if bVisible then
window.redraw()
end
end
function window.check_backing()
local w, h = parent.getSize()
if w ~= nWidth or h ~=- nHeight then
raw_resize(w, h)
end
end
function window.backing()
return parent
end
if bVisible then
window.redraw()
end
window.is_framebuffer = true
return window
end
return create_window

View File

@ -2,7 +2,7 @@
local function copy(tabl)
local new = {}
for k, v in pairs(tabl) do
if type(v) == "table" and tabl ~= v and v.COPY_EXACT == nil then
if type(v) == "table" and tabl ~= v then
new[k] = copy(v)
else
new[k] = v
@ -138,6 +138,18 @@ local function combine(segs)
end
return out
end
-- magic from http://lua-users.org/wiki/SplitJoin
-- split string into lines
local function lines(str)
local t = {}
local function helper(line)
table.insert(t, line)
return ""
end
helper((str:gsub("(.-)\r?\n", helper)))
return t
end
-- Fetch the contents of URL "u"
local function fetch(u)
@ -148,27 +160,12 @@ local function fetch(u)
end
-- Make a read handle for a string
-- PS#8FE487EF: Incompletely implemented handle behaviour lead to strange bugs on recent CC
local function make_handle(text)
local h = {}
local cursor = 1
local lines = lines(text)
local h = {line = 0}
function h.close() end
function h.readLine(with_trailing)
if cursor >= text:len() then return nil end
local lt_start, lt_end = text:find("\r?\n", cursor)
lt_start = lt_start or (text:len() + 1)
lt_end = lt_end or (text:len() + 1)
local seg = text:sub(cursor, with_trailing and lt_end or (lt_start - 1))
cursor = lt_end + 1
return seg
end
function h.read(count)
local count = count or 1
local seg = text:sub(cursor, cursor + count - 1)
cursor = cursor + count
return seg:len() ~= 0 and seg or nil
end
function h.readAll() local seg = text:sub(cursor) cursor = text:len() return seg:len() ~= 0 and seg or nil end
function h.readLine() h.line = h.line + 1 return lines[h.line] end
function h.readAll() return text end
return h
end
@ -181,7 +178,6 @@ local this_level_env = _G
-- Create a modified FS table which confines you to root and has some extra read-only pseudofiles.
local function create_FS(root, overlay)
local fs = fs
local mappings = make_mappings(root)
local vfstree = {
@ -217,7 +213,7 @@ local function create_FS(root, overlay)
end
end
local new = copy_some_keys {"getDir", "getName", "combine", "complete"} (fs)
local new = copy_some_keys {"getDir", "getName", "combine"} (fs)
function new.isReadOnly(path)
return path_in_overlay(new_overlay, path) or starts_with(canonicalize(path), "rom")
@ -342,7 +338,7 @@ local allowed_APIs = {
"http",
"pairs",
"ipairs",
-- getfenv, setfenv are modified to prevent sandbox escapes and defined in make_environment
-- getfenv, getfenv are modified to prevent sandbox escapes and defined in make_environment
"peripheral",
"table",
"string",
@ -377,26 +373,6 @@ local allowed_APIs = {
"~expect",
"__inext",
"periphemu",
"fs",
"debug",
"write",
"print",
"printError",
"read",
"colors",
"io",
"parallel",
"settings",
"vector",
"colours",
"keys",
"disk",
"help",
"paintutils",
"rednet",
"textutils",
"commands",
"window"
}
local gf, sf = getfenv, setfenv
@ -404,14 +380,16 @@ local gf, sf = getfenv, setfenv
-- Takes the root directory to allow access to,
-- a map of paths to either strings containing their contents or functions returning them
-- and a table of extra APIs and partial overrides for existing APIs
local function make_environment(API_overrides, current_process)
local env_host = string.format("YAFSS on %s", _HOST)
local function make_environment(root_directory, overlay, API_overrides)
local environment = copy_some_keys(allowed_APIs)(_G)
environment.fs = create_FS(root_directory, overlay)
-- if function is not from within the VM, return env from within sandbox
function environment.getfenv(arg)
local env
if type(arg) == "number" then return gf() end
if not env or type(env._HOST) ~= "string" or not env._HOST == env_host then
if not env or type(env._HOST) ~= "string" or not string.match(env._HOST, "YAFSS") then
return gf()
else
return env
@ -424,28 +402,37 @@ Allowing `setfenv` to operate on any function meant that privileged code could i
]]
function environment.setfenv(fn, env)
local nenv = gf(fn)
if not nenv or type(nenv._HOST) ~= "string" or not nenv._HOST == env_host then
if not nenv or type(nenv._HOST) ~= "string" or not string.match(nenv._HOST, "YAFSS") then
return false
end
return sf(fn, env)
end
local load = load
function environment.load(code, file, mode, env)
return load(code, file, mode, env or environment)
return load(code, file or "@<input>", mode or "t", env or environment)
end
if debug then
environment.debug = copy_some_keys {
"getmetatable",
"setmetatable",
"traceback",
"getinfo",
"getregistry"
}(debug)
end
environment._G = environment
environment._ENV = environment
environment._HOST = env_host
environment._HOST = string.format("YAFSS on %s", _HOST)
function environment.os.shutdown()
process.IPC(current_process, "power_state", "shutdown")
os.queueEvent("power_state", "shutdown")
while true do coroutine.yield() end
end
function environment.os.reboot()
process.IPC(current_process, "power_state", "reboot")
os.queueEvent("power_state", "reboot")
while true do coroutine.yield() end
end
@ -454,35 +441,39 @@ Allowing `setfenv` to operate on any function meant that privileged code could i
return environment
end
local function run(API_overrides, init, logger)
local current_process = process.running.ID
local function run(root_directory, overlay, API_overrides, init)
if type(init) == "table" and init.URL then init = fetch(init.URL) end
init = init or fetch "https://pastebin.com/raw/wKdMTPwQ"
local running = true
while running do
parallel.waitForAny(function()
local env = make_environment(API_overrides, current_process)
local env = make_environment(root_directory, overlay, API_overrides)
env.init_code = init
local out, err = load(init, "@[init]", "t", env)
if not out then error(err) end
local ok, err = pcall(out)
if not ok then logger("sandbox errored: %s", err) end
local ok, err = pcall(out)
if not ok then printError(err) end
end,
function()
while true do
local event, source, ty, spec = coroutine.yield "ipc"
if event == "ipc" and ty == "power_state" then -- coroutine.yield behaves weirdly with terminate
for _, p in pairs(process.list()) do
if process.is_ancestor(p, current_process) and p.ID ~= current_process and not p.thread then
process.signal(p.ID, process.signals.KILL)
local event, state = coroutine.yield "power_state"
if event == "power_state" then -- coroutine.yield behaves weirdly with terminate
if process then
local this_process = process.running.ID
for _, p in pairs(process.list()) do
if p.parent and p.parent.ID == this_process then
process.signal(p.ID, process.signals.KILL)
end
end
end
if spec == "shutdown" then running = false return
elseif spec == "reboot" then return end
if state == "shutdown" then running = false return
elseif state == "reboot" then return end
end
end
end)
sleep()
end
end
return { run = run, create_FS = create_FS }
return run

View File

@ -1,5 +1,13 @@
--[[
PotatOS Epenthesis - OS/Conveniently Self-Propagating System/Sandbox/Compilation of Useless Programs
Gollark (I) should delete potatOS
]]
--[[
PotatOS Hypercycle - OS/Conveniently Self-Propagating System/Sandbox/Compilation of Useless Programs
Best viewed in Internet Explorer 6.00000000000004 running on a Difference Engine emulated under MacOS 7 on a Pentium 3.
Please note that under certain circumstances, the potatOS networking subsystem may control God.
@ -14,17 +22,6 @@ Did you know? Because intellectual property law is weird, and any digitally stor
This license also extends to other PotatOS components or bundled software owned by me.
]]
local w, h = term.getSize()
local win = window.create(term.native(), 1, 1, w, h, true)
term.redirect(win)
local function capture_screen()
win.setVisible(true)
win.redraw()
end
local function uncapture_screen()
win.setVisible(false)
if process and process.IPC then pcall(process.IPC, "termd", "redraw_native") end
end
term.clear()
term.setCursorPos(1, 1)
if term.isColor() then
@ -60,6 +57,9 @@ local SPF = {
server = nil
}
if _G.shell and not _ENV.shell then _ENV.shell = _G.shell end
if _ENV.shell and not _G.shell then _G.shell = _ENV.shell end
os.pullEvent = coroutine.yield
local function get_registry(name)
@ -100,7 +100,6 @@ local function rot13(s)
return table.concat(out)
end
local debugtraceback = debug and debug.traceback
local logfile = fs.open("latest.log", "a")
local function add_log(...)
local args = {...}
@ -108,9 +107,6 @@ local function add_log(...)
local text = string.format(unpack(args))
if ccemux and ccemux.echo then ccemux.echo(text) end
local line = ("[%s] <%s> %s"):format(os.date "!%X %d/%m/%Y", (process and process.running and (process.running.name or tostring(process.running.ID))) or "[n/a]", text)
if get_setting "potatOS.traceback_logger" then
line = line .. "\n" .. debugtraceback()
end
logfile.writeLine(line)
logfile.flush() -- this should probably be infrequent enough that the performance impact is not very bad
-- primitive log rotation - logs should only be ~64KiB in total, which seems reasonable
@ -133,7 +129,7 @@ local function get_log()
return d
end
if SPF.server then add_log("SPF initialized: server %s", SPF.server or "none") end
if SPF.server then add_log("SPF initialized: server %s", SPF.server) end
-- print things to console for some reason? but only in CCEmuX
-- this ~~is being removed~~ is now gone but I am leaving this comment here for some reason
@ -226,6 +222,13 @@ local function copy(tabl)
return new
end
-- https://pastebin.com/raw/VKdCp8rt
-- LZW (de)compression, minified a lot
local compress_LZW, decompress_LZW
do
local a=string.char;local type=type;local select=select;local b=string.sub;local c=table.concat;local d={}local e={}for f=0,255 do local g,h=a(f),a(f,0)d[g]=h;e[h]=g end;local function i(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[j]=a(l,m)l=l+1;return k,l,m end;compress_LZW=function(n)if type(n)~="string"then error("string expected, got "..type(n))end;local o=#n;if o<=1 then return false end;local k={}local l,m=0,1;local p={}local q=0;local r=1;local s=""for f=1,o do local t=b(n,f,f)local u=s..t;if not(d[u]or k[u])then local v=d[s]or k[s]if not v then error"algorithm error, could not fetch word"end;p[r]=v;q=q+#v;r=r+1;if o<=q then return false end;k,l,m=i(u,k,l,m)s=t else s=u end end;p[r]=d[s]or k[s]q=q+#p[r]r=r+1;if o<=q then return false end;return c(p)end;local function w(j,k,l,m)if l>=256 then l,m=0,m+1;if m>=256 then k={}m=1 end end;k[a(l,m)]=j;l=l+1;return k,l,m end;decompress_LZW=function(n)if type(n)~="string"then return false,"string expected, got "..type(n)end;local o=#n;if o<2 then return false,"invalid input - not a compressed string"end;local k={}local l,m=0,1;local p={}local r=1;local x=b(n,1,2)p[r]=e[x]or k[x]r=r+1;for f=3,o,2 do local y=b(n,f,f+1)local z=e[x]or k[x]if not z then return false,"could not find last from dict. Invalid input?"end;local A=e[y]or k[y]if A then p[r]=A;r=r+1;k,l,m=w(z..b(A,1,1),k,l,m)else local B=z..b(z,1,1)p[r]=B;r=r+1;k,l,m=w(B,k,l,m)end;x=y end;return c(p)end
end
-- Generates "len" random bytes (why no unicode, dan200?!)
local function randbytes(len)
local out = ""
@ -236,7 +239,6 @@ local function randbytes(len)
end
local function clear_space(reqd)
capture_screen()
for _, i in pairs {
".potatOS-old-*",
"ecc",
@ -273,9 +275,8 @@ local function clear_space(reqd)
local path = v[1]
print("Deleting", path)
fs.delete(path)
if fs.getFreeSpace "/" > (reqd + 8192) then uncapture_screen() return end
if fs.getFreeSpace "/" > (reqd + 8192) then return end
end
uncapture_screen()
end
-- Write "c" to file "n"
@ -321,6 +322,44 @@ end
_G.fread = fread
_G.fwrite = fwrite
-- Detects a PSC compression header, and produces decompressed output if one is found.
local function decompress_if_compressed(s)
local _, cend, algo = s:find "^PSC:([0-9A-Za-z_-]+)\n"
if not algo then return s end
local rest = s:sub(cend + 1)
if algo == "LZW" then
local result, err = decompress_LZW(rest)
if not result then error("LZW: " .. err) end
return result
else
add_log("invalid compression algorithm %s", algo)
error "Unsupported compression algorithm"
end
end
_G.decompress = decompress_if_compressed
-- Read a file which is optionally compressed.
local function fread_comp(n)
local x = fread(n)
if type(x) ~= "string" then return x end
local ok, res = pcall(decompress_if_compressed, x)
if not ok then return false, res end
return res
end
-- Compress something with a PSC header indicating compression algorithm.
-- Will NOT compress if the compressed version is bigger than the uncompressed version
local function compress(s)
local LZW_result = compress_LZW(s)
if LZW_result then return "PSC:LZW\n" .. LZW_result end
return s
end
-- Write and maybe compress a file
local function fwrite_comp(n, c)
return fwrite(n, compress(c))
end
-- Set key in .settings
local function set(k, v)
settings.set(k, v)
@ -494,7 +533,7 @@ local function generate_disk_code()
)
end
-- Upgrade other disks to contain potatOS and/or load debug programs (mostly the "OmniDisk") off them.
-- Upgrade other disks to contain potatOS and/or load debug programs (mostly the "OmniDisk") off them.
local function process_disk(disk_side)
local mp = disk.getMountPath(disk_side)
if not mp then return end
@ -811,11 +850,10 @@ local function gen_uuid()
return table.concat(out)
end
-- PS#44BE67B6: ipairs somehow causing issues on CraftOS-PC
local function hexize(tbl)
local out = {}
for k = 1, #tbl do
out[k] = string.format("%02x", tbl[k])
for k, v in ipairs(tbl) do
out[k] = ("%02x"):format(v)
end
return table.concat(out)
end
@ -835,32 +873,18 @@ local function download_files(manifest_data, needed_files)
table.insert(fns, function()
add_log("downloading %s", file)
local url = base_URL .. "/" .. file
write "."
local h = assert(http.get(url, nil, true))
local x = h.readAll()
h.close()
local hexsha = hexize(sha256(x))
if (manifest_data.sizes and manifest_data.sizes[file] and manifest_data.sizes[file] ~= #x) or manifest_data.files[file] ~= hexsha then
error(("hash mismatch on %s %s (expected %s, got %s)"):format(file, url, manifest_data.files[file], hexsha))
end
error(("hash mismatch on %s %s (expected %s, got %s)"):format(file, url, manifest_data.files[file], hexsha)) end
fwrite(file, x)
write "."
count = count + 1
end)
end
print "running batch download"
-- concurrency limit
local cfns = {}
for i = 1, 4 do
table.insert(cfns, function()
while true do
local nxt = table.remove(fns)
if not nxt then return end
nxt()
end
end)
end
parallel.waitForAll(unpack(cfns))
parallel.waitForAll(unpack(fns))
print "done"
return count
end
@ -875,7 +899,6 @@ local function verify_update_sig(hash, sig)
return ecc.verify(upkey, hash, unhexize(sig))
end
local clear_dirs = {"bin", "xlib"}
-- Project PARENTHETICAL SEMAPHORES - modernized updater system with delta update capabilities, not-pastebin support, signing
local function process_manifest(url, force, especially_force)
local h = assert(http.get(url, nil, true)) -- binary mode, to avoid any weirdness
@ -885,7 +908,6 @@ local function process_manifest(url, force, especially_force)
local metadata = json.decode(txt:match "\n(.*)$")
local main_data_hash = hexize(sha256(main_data))
if main_data_hash ~= metadata.hash then
error(("hash mismatch: %s %s"):format(main_data_hash, metadata.hash))
end
@ -897,7 +919,6 @@ local function process_manifest(url, force, especially_force)
return false
end
end
capture_screen()
local ok, res
if metadata.sig then
@ -937,17 +958,6 @@ local function process_manifest(url, force, especially_force)
if #needs > 0 then
v = download_files(data, needs)
end
for _, c in pairs(clear_dirs) do
for _, d in pairs(fs.list(c)) do
local fullpath = fs.combine(c, d)
if not data.files[fullpath] then
add_log("deleting %s", fullpath)
fs.delete(fullpath)
end
end
end
set("potatOS.current_hash", metadata.hash)
registry.set("potatOS.current_manifest", data)
return v
@ -965,7 +975,6 @@ local function install(force)
local res = process_manifest(manifest, force)
if (res == 0 or res == false) and not force then
uncapture_screen()
return false
end
@ -993,6 +1002,17 @@ local function install(force)
os.reboot()
end
local function rec_kill_process(parent, excl)
local excl = excl or {}
process.signal(parent, process.signals.KILL)
for _, p in pairs(process.list()) do
if p.parent.ID == parent and not excl[p.ID] then
process.signal(p.ID, process.signals.KILL)
rec_kill_process(p.ID, excl)
end
end
end
local function critical_error(err)
term.clear()
term.setCursorPos(1, 1)
@ -1014,131 +1034,87 @@ local function critical_error(err)
end
end
end
local function run_with_sandbox()
-- Load a bunch of necessary PotatoLibraries™
-- if fs.exists "lib/bigfont" then os.loadAPI "lib/bigfont" end
if fs.exists "lib/gps.lua" then
os.loadAPI "lib/gps.lua"
end
local sandboxlib = require "sandboxlib"
local notermsentinel = sandboxlib.create_sentinel "no-terminate"
local processhasgrant = process.has_grant
local processrestriction = process.restriction
local processinfo = process.info
local processgetrunning = process.get_running
function _G.os.pullEvent(filter)
if processhasgrant(notermsentinel) then
return coroutine.yield(filter)
else
local result = {coroutine.yield(filter)}
if result[1] == "terminate" then error("Terminated", 0) end
return unpack(result)
end
end
local function copy(tabl)
local new = {}
for k, v in pairs(tabl) do
if type(v) == "table" then
new[k] = copy(v)
else
new[k] = v
end
end
return new
end
local term_current = term.current()
local term_native = term.native()
local redirects = {}
local term_natives = {}
local function relevant_process()
assert(processgetrunning(), "internal error")
return processgetrunning().thread_parent and processgetrunning().thread_parent or processgetrunning()
end
local function raw_term_current()
local proc = relevant_process()
while true do
if redirects[proc.ID] then return redirects[proc.ID] end
if not proc.parent then
break
end
proc = proc.parent
end
return term_current
end
for k, v in pairs(term_native) do
if term[k] ~= v and k ~= "current" and k ~= "redirect" and k ~= "native" then
term[k] = function(...)
return raw_term_current()[k](...)
end
end
end
function term.current()
return copy(raw_term_current())
end
function term.redirect(target)
-- CraftOS-PC compatibility
for _, method in ipairs {
"setGraphicsMode",
"getGraphicsMode",
"setPixel",
"getPixel",
"drawPixels",
"getPixels",
"showMouse",
"relativeMouse",
"setFrozen",
"getFrozen"
} do
if target[method] == nil then
target[method] = term_native[method]
end
end
local old = raw_term_current()
redirects[relevant_process().ID] = target
return copy(old)
end
function term.native()
local id = relevant_process().ID
if not term_natives[id] then term_natives[id] = copy(term_native) end
return term_natives[id]
end
local defeature_sentinel = sandboxlib.create_sentinel "defeature"
local tw = term.write
function term.write(text)
if type(text) == "string" and processrestriction(defeature_sentinel) then text = text:gsub("bug", "feature") end
return tw(text)
end
-- Hook up the debug registry to the potatOS Registry.
debug_registry_mt.__index = function(_, k) return registry.get(k) end
debug_registry_mt.__newindex = function(_, k, v) return registry.set(k, v) end
local function fproxy(file)
local ok, t = pcall(fread, file)
local ok, t = pcall(fread_comp, file)
if not ok or not t then return 'printError "Error. Try again later, or reboot, or run upd."' end
return t
end
-- Localize a bunch of variables. Does this help? I have no idea. This is old code.
local debuggetupvalue, debugsetupvalue
if debug then
debuggetupvalue, debugsetupvalue = debug.getupvalue, debug.setupvalue
end
local global_potatOS = _ENV.potatOS
-- Try and get the native "peripheral" API via haxx.
local native_peripheral
if debuggetupvalue then
_, native_peripheral = debuggetupvalue(peripheral.call, 2)
end
local uuid = settings.get "potatOS.uuid"
-- Generate a build number from the first bit of the verhash
local full_build = settings.get "potatOS.current_hash"
_G.build_number = full_build:sub(1, 8)
add_log("build number is %s, uuid is %s", _G.build_number, uuid)
local env = _G
local counter = 1
local function privileged_execute(code, raw_signature, chunk_name, args)
local args = args or {}
local signature = unhexize(raw_signature)
if verify(code, signature) then
add_log("privileged execution begins - sig %s", raw_signature)
local result = nil
local this_counter = counter
counter = counter + 1
process.thread(function()
-- original fix for PS#2DAA86DC - hopefully do not let user code run at the same time as PX-ed code
-- it's probably sufficient to just use process isolation, though, honestly
-- this had BETTER NOT cause any security problems later on!
--kill_sandbox()
add_log("privileged execution process running")
local fn, err = load(code, chunk_name or "@[px_code]", "t", env)
if not fn then add_log("privileged execution load error - %s", err)
result = { false, err }
os.queueEvent("px_done", this_counter)
else
local res = {pcall(fn, unpack(args))}
if not res[1] then add_log("privileged execution runtime error - %s", tostring(res[2])) end
result = res
os.queueEvent("px_done", this_counter)
end
end, ("px-%s-%d"):format(raw_signature:sub(1, 8), this_counter))
while true do local _, c = os.pullEvent "px_done" if c == this_counter then break end end
return true, unpack(result)
else
report_incident("invalid privileged execution signature",
{"security", "px_signature"},
{
code = code,
extra_meta = { signature = raw_signature, chunk_name = chunk_name }
})
return false
end
end
local is_uninstalling = false
-- PotatOS API functionality
-- "pure" is meant loosely
local pure_functions_list = {"gen_uuid", "randbytes", "hexize", "unhexize", "rot13", "create_window_buf"}
local pure_functions = {}
for k, v in pairs(pure_functions_list) do pure_functions[v] = true end
local potatOS = {
ecc = require "ecc",
ecc168 = require "ecc-168",
@ -1152,15 +1128,19 @@ local function run_with_sandbox()
add_log = add_log,
ancestry = ancestry,
gen_count = gen_count,
compress_LZW = compress_LZW,
decompress_LZW = decompress_LZW,
decompress = decompress_if_compressed,
compress = compress,
privileged_execute = privileged_execute,
unhexize = unhexize,
hexize = hexize,
randbytes = randbytes,
report_incident = report_incident,
get_location = get_location,
get_setting = get_setting,
get_host = get_host,
registry_get = registry.get,
registry_set = registry.set,
native_peripheral = native_peripheral,
registry = registry,
get_ip = function()
return external_ip
end,
@ -1190,7 +1170,7 @@ local function run_with_sandbox()
end,
-- Updates potatOS
update = function()
process.IPC("potatoupd", "trigger_update", true)
os.queueEvent("trigger_update", true)
end,
-- Messes up 1 out of 10 keypresses.
evilify = function()
@ -1211,7 +1191,6 @@ local function run_with_sandbox()
-- it can fake keyboard inputs via queueEvent (TODO: sandbox that?)
begin_uninstall_process = function()
if settings.get "potatOS.pjals_mode" then error "Protocol Omega Initialized. Access Denied." end
capture_screen()
is_uninstalling = true
math.randomseed(secureish_randomseed)
secureish_randomseed = math.random(0xFFFFFFF)
@ -1222,11 +1201,11 @@ local function run_with_sandbox()
print("Please find the prime factors of the following number (or enter 'quit') to exit:", num)
write "Factor 1: "
local r1 = read()
if r1 == "quit" then uncapture_screen() is_uninstalling = false return end
if r1 == "quit" then is_uninstalling = false return end
local f1 = tonumber(r1)
write "Factor 2: "
local r2 = read()
if r2 == "quit" then uncapture_screen() is_uninstalling = false return end
if r2 == "quit" then is_uninstalling = false return end
local f2 = tonumber(r2)
if (f1 == p1 and f2 == p2) or (f1 == p2 and f2 == p1) then
term.clear()
@ -1240,18 +1219,14 @@ local function run_with_sandbox()
print("Factors", f1, f2, "invalid.", p1, p2, "expected. This incident has been reported.")
end
is_uninstalling = false
uncapture_screen()
end,
term_screenshot = term.screenshot,
enable_backing = win.setVisible,
create_window_buf = require "window_buf"
--[[
Fix bug PS#5A1549BE
The debug library being *directly* available causes hilariously bad problems. This is a bad idea and should not be available in unmodified form. Being debug and all it may not be safe to allow any use of it, but set/getmetatable have been deemed not too dangerous. Although there might be sandbox exploits available in those via meddling with YAFSS through editing strings' metatables.
]]
--debug = (potatOS or external_env).debug -- too insecure, this has been removed, why did I even add this.
}
-- Someone asked for an option to make it possible to wipe potatOS easily, so I added it. The hedgehogs are vital to its operation.
-- See https://hackage.haskell.org/package/hedgehog-classes for further information.
if settings.get "potatOS.removable" then
@ -1266,47 +1241,234 @@ local function run_with_sandbox()
end
end
end
local privapid = process.spawn(function()
while true do
local event, source, sent, fn, args = coroutine.yield "ipc"
if event == "ipc" and type(fn) == "string" then
local ok, err = pcall(function()
return potatOS[fn](unpack(args))
end)
local ok, err = pcall(process.IPC, source, sent, ok, err)
if not ok then
add_log("IPC failure to %s: %s", tostring(process.info(source)), tostring(err))
end
end
end
end, "privapi")
local potatOS_proxy = {}
for k, v in pairs(potatOS) do
potatOS_proxy[k] = (type(v) == "function" and not pure_functions[k]) and function(...)
local sent = {}
process.IPC(privapid, sent, k, { ... })
while true do
local _, source, rsent, ok, err = coroutine.yield "ipc"
if source == privapid and rsent == sent then
if not ok then error(err) end
return err
end
end
end or v
end
-- Provide many, many useful or not useful programs to the potatOS shell.
local FS_overlay = {
["secret/.pkey"] = fproxy "signing-key.tbl",
["secret/log"] = function() return potatOS_proxy.get_log() end,
["secret/log"] = function() return potatOS.get_log() end,
["/rom/programs/clear_space.lua"] = [[potatOS.clear_space(4096)]],
["/rom/programs/build.lua"] = [[
print("Short hash", potatOS.build)
print("Full hash", potatOS.full_build)
local mfst = potatOS.registry.get "potatOS.current_manifest"
if mfst then
print("Counter", mfst.build)
print("Built at (local time)", os.date("%Y-%m-%d %X", mfst.timestamp))
print("Downloaded from", mfst.manifest_URL)
local verified = mfst.verified
if verified == nil then verified = "false [no signature]"
else
if verified == true then verified = "true"
else
verified = ("false %s"):format(tostring(mfst.verification_error))
end
end
print("Signature verified", verified)
else
print "Manifest not found in registry. Extended data unavailable."
end
]],
["/rom/programs/id.lua"] = [[
print("ID", os.getComputerID())
print("Label", os.getComputerLabel())
print("UUID", potatOS.uuid)
print("Build", potatOS.build)
print("Host", _ORIGHOST or _HOST)
local disks = {}
for _, n in pairs(peripheral.getNames()) do
if peripheral.getType(n) == "drive" then
local d = peripheral.wrap(n)
if d.hasData() then
table.insert(disks, {n, tostring(d.getDiskID() or "[ID?]"), d.getDiskLabel()})
end
end
end
if #disks > 0 then
print "Disks:"
textutils.tabulate(unpack(disks))
end
if potatOS.get_ip() then
print("IP", potatOS.get_ip())
end
]],
["/rom/programs/log.lua"] = [[
local args = table.concat({...}, " ")
local logtext
if args:match "old" then
logtext = potatOS.read "old.log"
else
logtext = potatOS.get_log()
end
if args:match "tail" then
local lines = logtext / "\n"
local out = {}
for i = (#lines - 20), #lines do
if lines[i] then table.insert(out, lines[i]) end
end
logtext = table.concat(out, "\n")
end
textutils.pagedPrint(logtext)
]],
["/rom/programs/init-screens.lua"] = [[potatOS.init_screens(); print "Done!"]],
["/rom/programs/game-mode.lua"] = [[
potatOS.evilify()
print "GAME KEYBOARD enabled."
potatOS.init_screens()
print "GAME SCREEN enabled."
print "Activated GAME MODE."
--bigfont.bigWrite "GAME MODE."
--local x, y = term.getCursorPos()
--term.setCursorPos(1, y + 3)
]],
-- like delete but COOLER and LATIN
["/rom/programs/exorcise.lua"] = [[
for _, wcard in pairs{...} do
for _, path in pairs(fs.find(wcard)) do
fs.ultradelete(path)
local n = potatOS.lorem():gsub("%.", " " .. path .. ".")
print(n)
end
end
]],
["/rom/programs/upd.lua"] = 'potatOS.update()',
["/rom/programs/lyr.lua"] = 'print(string.format("Layers of virtualization >= %d", potatOS.layers()))',
["/rom/programs/uninstall.lua"] = [[
if potatOS.actually_really_uninstall then potatOS.actually_really_uninstall "76fde5717a89e332513d4f1e5b36f6cb" os.reboot()
else
potatOS.begin_uninstall_process()
end
]],
["/rom/programs/very-uninstall.lua"] = "shell.run 'loading' term.clear() term.setCursorPos(1, 1) print 'Actually, nope.'",
["/rom/programs/chuck.lua"] = "print(potatOS.chuck_norris())",
["/rom/programs/maxim.lua"] = "print(potatOS.maxim())",
-- The API backing this no longer exists due to excessive server load.
-----["/rom/programs/dwarf.lua"] = "print(potatOS.dwarf())",
["/rom/programs/norris.lua"] = "print(string.reverse(potatOS.chuck_norris()))",
["/rom/programs/fortune.lua"] = "print(potatOS.fortune())",
["/rom/programs/potatonet.lua"] = "potatOS.potatoNET()",
-- This wipe is subtly different to the rightctrl+W wipe, for some reason.
["/rom/programs/wipe.lua"] = "print 'Foolish fool.' shell.run '/rom/programs/delete *' potatOS.update()",
-- Run edit without a run option
["/rom/programs/licenses.lua"] = "local m = multishell multishell = nil shell.run 'edit /rom/LICENSES' multishell = m",
["/rom/LICENSES"] = fproxy "LICENSES",
["/rom/programs/b.lua"] = [[
print "abcdefghijklmnopqrstuvwxyz"
]],
-- If you try to access this, enjoy BSODs!
["/rom/programs/BSOD.lua"] = [[
local w, h = term.getSize()
polychoron.BSOD(potatOS.randbytes(math.random(0, w * h)))
os.pullEvent "key"
]],
-- Tau is better than Pi. Change my mind.
["/rom/programs/tau.lua"] = 'if potatOS.tau then textutils.pagedPrint(potatOS.tau) else error "PotatOS tau missing - is PotatOS correctly installed?" end',
-- I think this is just to nest it or something. No idea if it's different to the next one.
["/secret/processes"] = function()
return tostring(process.list())
end,
["/rom/heavlisp_lib/stdlib.hvl"] = fproxy "stdlib.hvl"
["/rom/programs/dump.lua"] = [[
libdatatape.write(peripheral.find "tape_drive", fs.dump(...))
]],
["/rom/programs/load.lua"] = [[
fs.load(libdatatape.read(peripheral.find "tape_drive"), ...)
]],
-- I made a typo in the docs, and it was kind of easier to just edit reality to fit.
-- It said "est something whatever", and... well, this is "est", and it sets values in the PotatOS Registry.
["/rom/programs/est.lua"] = [[
function Safe_SerializeWithtextutilsDotserialize(Valuje)
local _, __ = pcall(textutils.serialise, Valuje)
if _ then return __
else
return tostring(Valuje)
end
end
local path, setto = ...
path = path or ""
if setto ~= nil then
local x, jo, jx = textutils.unserialise(setto), pcall(json.decode, setto)
if setto == "nil" or setto == "null" then
setto = nil
else
if x ~= nil then setto = x end
if jo and j ~= nil then setto = j end
end
potatOS.registry.set(path, setto)
print(("Value of registry entry %s set to:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(setto)))
else
textutils.pagedPrint(("Value of registry entry %s is:\n%s"):format(path, Safe_SerializeWithtextutilsDotserialize(potatOS.registry.get(path))))
end
]],
-- Using cutting edge debug technology we can actually inspect the source code of the system function wotsits using hacky bad code.
["/rom/programs/viewsource.lua"] = [[
local function try_files(lst)
for _, v in pairs(lst) do
local z = potatOS.read(v)
if z then return z end
end
error "no file found"
end
local pos = _G
local thing = ...
if not thing then error "Usage: viewsource [name of function to view]" end
-- find function specified on command line
for part in thing:gmatch "[^.]+" do
pos = pos[part]
if not pos then error(thing .. " does not exist: " .. part) end
end
local info = debug.getinfo(pos)
if not info.linedefined or not info.lastlinedefined or not info.source or info.lastlinedefined == -1 then error "Is this a Lua function?" end
local sourcen = info.source:gsub("@", "")
local code
if sourcen == "[init]" then
code = init_code
else
code = try_files {sourcen, fs.combine("lib", sourcen), fs.combine("bin", sourcen), fs.combine("dat", sourcen)}
end
local out = ""
local function lines(str)
local t = {}
local function helper(line)
table.insert(t, line)
return ""
end
helper((str:gsub("(.-)\r?\n", helper)))
return t
end
for ix, line in pairs(lines(code)) do
if ix >= info.linedefined and ix <= info.lastlinedefined then
out = out .. line .. "\n"
end
end
local filename = ".viewsource-" .. thing
local f = fs.open(filename, "w")
f.write(out)
f.close()
shell.run("edit", filename)
fs.delete(filename)
]],
["/rom/programs/regset.lua"] = [[
-- Wait, why do we have this AND est?
local key, value = ...
key = key or ""
if not value then print(textutils.serialise(potatOS.registry.get(key)))
else
if value == "" then value = nil
elseif textutils.unserialise(value) ~= nil then value = textutils.unserialise(value) end
potatOS.registry.set(key, value)
end
]],
["/rom/heavlisp_lib/stdlib.hvl"] = fproxy "stdlib.hvl",
["/rom/programs/ctime.lua"] = [[
for _, info in pairs(process.list()) do
print(("%s %f %f"):format(info.name or info.ID, info.execution_time, info.ctime))
end
]]
}
for _, file in pairs(fs.list "bin") do
@ -1317,17 +1479,25 @@ local function run_with_sandbox()
FS_overlay[fs.combine("rom/potato_xlib", file)] = fproxy(fs.combine("xlib", file))
end
local osshutdown = os.shutdown
local osreboot = os.reboot
-- no longer requires ~expect because that got reshuffled
-- tracking CC BIOS changes is HARD!
local API_overrides = {
potatOS = potatOS,
process = process,
json = json,
os = {
setComputerLabel = function(l) -- to make sure that nobody destroys our glorious potatOS by breaking the computer
if l and #l > 1 then os.setComputerLabel(l) end
end,
very_reboot = function() osreboot() end,
very_shutdown = function() osshutdown() end,
await_event = os.await_event
},
_VERSION = _VERSION,
potatOS = potatOS_proxy
polychoron = polychoron, -- so that nested instances use our existing process manager system, as polychoron detects specifically *its* presence and not just generic "process"
}
--[[
@ -1396,41 +1566,28 @@ local function run_with_sandbox()
timer = os.startTimer(1 + math.random(0, compcount * 2))
end
end
end, "netd", { grants = { [notermsentinel] = true }, restrictions = {} })
require "metatable_improvements"(potatOS_proxy.add_log, potatOS_proxy.report_incident)
local yafss = require "yafss"
local fss_sentinel = sandboxlib.create_sentinel "fs-sandbox"
local debug_sentinel = sandboxlib.create_sentinel "constrained-debug"
local sandbox_filesystem = yafss.create_FS("potatOS", FS_overlay)
_G.fs = sandboxlib.dispatch_if_restricted(fss_sentinel, _G.fs, sandbox_filesystem)
_G.debug = sandboxlib.allow_whitelisted(debug_sentinel, _G.debug, {
"traceback",
"getinfo",
"getregistry"
}, { getmetatable = getmetatable })
end, "netd")
-- Yes, you can disable the backdo- remote debugging services (oops), with this one simple setting.
-- Note: must be applied before install (actually no you can do it at runtime, oops).
-- Note: must be applied before install.
if not get_setting "potatOS.disable_backdoors" then
process.spawn(disk_handler, "potatodisk")
process.spawn(websocket_remote_debugging, "potatows")
end
local init_code = fread "potatobios.lua"
-- Load PotatoBIOS
process.spawn(function() yafss.run(
local init_code = fread_comp "potatobios.lua"
-- Spin up the "VM", with PotatoBIOS.
process.spawn(function() require "yafss"(
"potatOS",
FS_overlay,
API_overrides,
init_code,
potatOS_proxy.add_log
) end, "sandbox", { restrictions = { [fss_sentinel] = true, [debug_sentinel] = true, [defeature_sentinel] = true } })
function(e) critical_error(e) end
) end, "sandbox")
add_log "sandbox started"
end
return function(...)
local command = table.concat({...}, " ")
add_log("command line is %q", command)
-- Removes whitespace. I don't actually know what uses this either.
local function strip_whitespace(text)
@ -1458,15 +1615,7 @@ return function(...)
if config.get "romReadOnly" ~= false then pcall(config.set, "romReadOnly", false) end -- TODO: do something COOL with this.
end
if not process or not fs.exists "potatobios.lua" or not fs.exists "autorun.lua" then -- Polychoron not installed, so PotatOS isn't.
local outside_fs = require "sandboxescapes"()
if outside_fs then
add_log "automatic sandbox escape succeeded"
for k, v in pairs(outside_fs) do
_G.fs[k] = v
end
end
add_log "running installation"
if not polychoron or not fs.exists "potatobios.lua" or not fs.exists "autorun.lua" then -- Polychoron not installed, so PotatOS Tau isn't.
install(true)
else
process.spawn(function() -- run update task in kindofbackground process
@ -1479,11 +1628,11 @@ return function(...)
-- Spread out updates a bit to reduce load on the server.
local timer = os.startTimer(300 + (os.getComputerID() % 100) - 50)
while true do
local ev, arg, arg2, arg3 = coroutine.yield { timer = true, ipc = true }
local ev, arg = coroutine.yield { timer = true, trigger_update = true }
if ev == "timer" and arg == timer then
break
elseif ev == "ipc" and arg2 == "trigger_update" then
pcall(install, arg3)
elseif ev == "trigger_update" then
pcall(install, arg)
end
end
end

View File

@ -1,4 +1,4 @@
local DEBUG_MODE = settings.get "potatOS.polychoron_debug"
local version = "1.6"
-- Localize frequently used functions for performance
local osepoch = os.epoch
@ -8,21 +8,9 @@ local coroutineresume = coroutine.resume
local coroutineyield = coroutine.yield
local coroutinestatus = coroutine.status
local tostring = tostring
local coroutinecreate = coroutine.create
local pairs = pairs
local ipairs = ipairs
local setmetatable = setmetatable
local tableinsert = table.insert
local assert = assert
local error = error
local tableunpack = table.unpack
local debugtraceback = debug and debug.traceback
local osqueueevent = os.queueEvent
local ccemuxnanoTime
local ccemuxecho
if ccemux then
ccemuxnanoTime = ccemux.nanoTime
ccemuxecho = ccemux.echo
end
-- Return a time of some sort. Not used to provide "objective" time measurement, just for duration comparison
@ -37,30 +25,6 @@ end
local processes = {}
_G.process = {}
local function copy(t)
local out = {}
for k, v in pairs(t) do
out[k] = v
end
return out
end
local statuses = {
DEAD = "dead",
ERRORED = "errored",
OK = "ok",
STOPPED = "stopped"
}
process.statuses = copy(statuses)
local signals = {
START = "start",
STOP = "stop",
TERMINATE = "terminate",
KILL = "kill"
}
process.signals = copy(signals)
-- Allow getting processes by name, and nice process views from process.list()
local process_list_mt = {
__tostring = function(ps)
@ -72,10 +36,6 @@ local process_list_mt = {
return o:gsub("\n$", "") -- strip trailing newline
end,
__index = function(tabl, key)
if type(key) == "table" and key.ID then return tabl[key.ID] end
for i, p in pairs(tabl) do
if p.name == key and p.status ~= statuses.DEAD then return p end
end
for i, p in pairs(tabl) do
if p.name == key then return p end
end
@ -88,14 +48,28 @@ setmetatable(processes, process_list_mt)
function _G.sleep(time)
time = time or 0
local t = os.startTimer(time)
local start = osclock()
local start = os.clock()
local ev, arg, tdiff
repeat
ev, arg = os.pullEvent()
until (ev == "timer" and arg == t) or (osclock() - start) > time
until (ev == "timer" and arg == t) or (os.clock() - start) > time
end
process.statuses = {
DEAD = "dead",
ERRORED = "errored",
OK = "ok",
STOPPED = "stopped"
}
process.signals = {
START = "start",
STOP = "stop",
TERMINATE = "terminate",
KILL = "kill"
}
-- Gets the first key in a table with the given value
local function get_key_with_value(t, v)
for tk, tv in pairs(t) do
@ -125,33 +99,26 @@ local allow_event = {
}
local function process_to_info(p)
if DEBUG_MODE then return p end
if not p then return nil end
local out = {}
for k, v in pairs(p) do
if k == "parent" and v ~= nil then
out.parent = process_to_info(v)
elseif k == "thread_parent" and v ~= nil then
out.thread_parent = process_to_info(v)
else
-- PS#85DD8AFC
-- Through some bizarre environment weirdness even exposing the function causes security risks. So don't.
if k ~= "coroutine" and k ~= "function" and k ~= "table" then
if k ~= "coroutine" and k ~= "function" then
out[k] = v
end
end
end
out.capabilities = { restrictions = copy(p.capabilities.restrictions), grants = copy(p.capabilities.grants) }
setmetatable(out, process_metatable)
return out
end
-- Fancy BSOD
local function BSOD(e)
if false then
if _G.add_log then _G.add_log("failure recorded: %s", e) end
if _G.add_log and debugtraceback then _G.add_log("stack traceback: %s", debugtraceback()) end
if _G.add_log then _G.add_log("BSOD recorded: %s", e) end
if term.isColor() then term.setBackgroundColor(colors.blue) term.setTextColor(colors.white)
else term.setBackgroundColor(colors.white) term.setTextColor(colors.black) end
@ -160,15 +127,14 @@ local function BSOD(e)
term.setCursorPos(1, 1)
print(e)
end
end
local running
-- Apply "event" to "proc"
-- Where most important stuff happens
local function tick(proc, event)
if not proc then error "Internal error: No such process" end
if running then return end
if not proc then error "No such process" end
if process.running and process.running.ID == proc.ID then return end
-- Run any given event preprocessor on the event
-- Actually don't, due to (hypothetical) PS#D7CD76C0-like exploits
@ -179,15 +145,16 @@ local function tick(proc, event)
end
]]
-- If coroutine is dead, just ignore it and set its status to dead
if coroutinestatus(proc.coroutine) == "dead" or proc.status == statuses.DEAD then
proc.status = statuses.DEAD
if proc.thread then processes[proc.ID] = nil end
return
-- If coroutine is dead, just ignore it but set its status to dead
if coroutinestatus(proc.coroutine) == "dead" then
proc.status = process.statuses.DEAD
if proc.ephemeral then
processes[proc.ID] = nil
end
end
-- If coroutine ready and filter matches or event is allowed, run it, set the running process in its environment,
-- get execution time, and run error handler if errors happen.
if proc.status == statuses.OK and (proc.filter == nil or proc.filter == event[1] or (type(proc.filter) == "table" and proc.filter[event[1]]) or allow_event[event[1]]) then
if proc.status == process.statuses.OK and (proc.filter == nil or proc.filter == event[1] or (type(proc.filter) == "table" and proc.filter[event[1]]) or allow_event[event[1]]) then
process.running = process_to_info(proc)
running = proc
local start_time = time()
@ -199,7 +166,7 @@ local function tick(proc, event)
if proc.error_handler then
proc.error_handler(res)
else
proc.status = statuses.ERRORED
proc.status = process.statuses.ERRORED
proc.error = res
if res ~= "Terminated" then -- programs terminating is normal, other errors not so much
BSOD(stringformat("Process %s has crashed!\nError: %s", proc.name or tostring(proc.ID), tostring(res)))
@ -208,134 +175,47 @@ local function tick(proc, event)
else
proc.filter = res
end
running = nil
process.running = nil
end
end
local queue = {}
local events_are_queued = false
local function find_all_in_group(id)
local proc = processes[id]
if proc.thread then
proc = proc.thread_parent
end
local procs = {proc}
for _, p in pairs(processes) do
if p.thread_parent == proc then
tableinsert(procs, p)
end
end
return procs
end
local function enqueue(id, event)
events_are_queued = true
for _, tg in pairs(find_all_in_group(id)) do
local id = tg.ID
queue[id] = queue[id] or {}
tableinsert(queue[id], event)
end
end
function process.get_running()
return process_to_info(running)
end
function process.IPC(target, ...)
if not processes[target] then error(stringformat("No such process %s.", tostring(target))) end
enqueue(processes[target].ID, { "ipc", running.ID, ... })
return running
end
-- Send/apply the given signal to the given process
local function apply_signal(proc, signal)
enqueue(proc.ID, { "signal", signal, running.ID })
if signal == signals.TERMINATE then
enqueue(proc.ID, { "terminate" })
local rID = nil
if process.running then rID = process.running.ID end
tick(proc, { "signal", signal, rID })
-- START - starts stopped process
if signal == process.signals.START and proc.status == process.statuses.STOPPED then
proc.status = process.statuses.OK
-- STOP stops started process
elseif signal == process.signals.STOP and proc.status == process.statuses.OK then
proc.status = process.statuses.STOPPED
elseif signal == process.signals.TERMINATE then
proc.terminated_time = os.clock()
tick(proc, { "terminate" })
elseif signal == process.signals.KILL then
proc.status = process.statuses.DEAD
end
for _, proc in pairs(find_all_in_group(proc.ID)) do
-- START - starts stopped process
if signal == signals.START and proc.status == statuses.STOPPED then
proc.status = statuses.OK
-- STOP stops started process
elseif signal == signals.STOP and proc.status == statuses.OK then
proc.status = statuses.STOPPED
elseif signal == signals.TERMINATE then
proc.terminated_time = osclock()
elseif signal == signals.KILL then
proc.status = statuses.DEAD
end
end
end
local function ensure_no_metatables(x)
if type(x) ~= "table" then return end
assert(getmetatable(x) == nil)
for k, v in pairs(x) do
ensure_no_metatables(v)
ensure_no_metatables(k)
end
end
local root_capability = {"root"}
local function ensure_capabilities_subset(x, orig)
x.grants = x.grants or {}
x.restrictions = x.restrictions or {}
ensure_no_metatables(x)
assert(type(x.restrictions) == "table")
assert(type(x.grants) == "table")
if orig.grants[root_capability] then return end
for restriction, value in pairs(orig.restrictions) do
x.restrictions[restriction] = value
end
for grant, enabled in pairs(x.grants) do
if enabled and not orig.grants[grant] then
x.grants[grant] = false
end
end
end
local function are_capabilities_subset(x, orig)
if orig.grants[root_capability] then return true end
for restriction, value in pairs(orig.restrictions) do
if x.restrictions[restriction] ~= value then
return false
end
end
for grant, enabled in pairs(x.grants) do
if enabled and not orig.grants[grant] then
return false
end
end
return true
end
local next_ID = 1
local function spawn(fn, name, thread, capabilities)
name = tostring(name)
function process.spawn(fn, name, extra)
local this_ID = next_ID
if not capabilities then
capabilities = running.capabilities
end
if running then ensure_capabilities_subset(capabilities, running.capabilities) end
local proc = {
coroutine = coroutinecreate(fn),
coroutine = coroutine.create(fn),
name = name,
status = statuses.OK,
status = process.statuses.OK,
ID = this_ID,
parent = running,
parent = process.running,
["function"] = fn,
ctime = 0,
capabilities = capabilities
ctime = 0
}
if thread then
proc.thread_parent = running.thread_parent or running
proc.thread = true
proc.parent = running.parent
end
if extra then for k, v in pairs(extra) do proc[k] = v end end
setmetatable(proc, process_metatable)
processes[this_ID] = proc
@ -343,13 +223,9 @@ local function spawn(fn, name, thread, capabilities)
return this_ID
end
function process.spawn(fn, name, capabilities)
return spawn(fn, name, nil, capabilities)
end
function process.thread(fn, name)
local parent = running.name or tostring(running.ID)
return spawn(fn, ("%s_%s_%04x"):format(name or "th", parent, math.random(0, 0xFFFF)), true)
local parent = process.running.name or tostring(process.running.ID)
process.spawn(fn, ("%s_%s_%04x"):format(name or "thread", parent, math.random(0, 0xFFFF)), { ephemeral = true })
end
-- Sends a signal to the given process ID
@ -358,14 +234,6 @@ function process.signal(ID, signal)
apply_signal(processes[ID], signal)
end
function process.has_grant(g)
return running.capabilities.grants[g] or running.capabilities.grants[root_capability] or false
end
function process.restriction(r)
return running.capabilities.restrictions[r] or nil
end
-- PS#F7686798
-- Prevent mutation of processes through exposed API to prevent PS#D7CD76C0-like exploits
-- List all processes
@ -381,60 +249,32 @@ function process.info(ID)
return process_to_info(processes[ID])
end
function os.queueEvent(...)
enqueue(running.ID, {...})
end
local function ancestry_includes(proc, anc)
repeat
if proc == anc then
return true
end
proc = proc.parent
until not proc
return false
end
function process.is_ancestor(proc, anc)
return ancestry_includes(processes[proc], processes[anc])
end
function process.queue_in(ID, ...)
local parent = processes[ID]
if not parent then error(stringformat("No such process %s.", tostring(ID))) end
for ID, proc in pairs(processes) do
if ancestry_includes(proc, parent) and are_capabilities_subset(proc.capabilities, running.capabilities) and not proc.thread then
enqueue(proc.ID, {...})
end
end
end
local dummy_event = ("%07x"):format(math.random(0, 0xFFFFFFF))
-- Run main event loop
local function run_loop()
while true do
if events_are_queued then
events_are_queued = false
for target, events in pairs(queue) do
for _, event in ipairs(events) do
tick(processes[target], event)
end
queue[target] = nil
end
osqueueevent(dummy_event)
else
local ev = {coroutineyield()}
if ev[1] ~= dummy_event then
for ID, proc in pairs(processes) do
tick(proc, ev)
end
end
local ev = {coroutineyield()}
for ID, proc in pairs(processes) do
tick(proc, ev)
end
end
end
local function boot()
if ccemuxecho then ccemuxecho("TLCO executed " .. (debugtraceback and debugtraceback() or "succesfully")) end
local base_processes = {
["main"] = function() os.run({}, "autorun.lua") end,
["rednetd"] = function()
-- bodge, because of the stupid rednet bRunning thing
local old_error = error
_G.error = function() _G.error = old_error end
rednet.run()
end
}
-- hacky magic to run our code and not the BIOS stuff
-- this terminates the shell, which crashes the BIOS, which then causes an error, which is printed with printError
local old_printError = _G.printError
function _G.printError()
_G.printError = old_printError
-- Multishell must die.
term.redirect(term.native())
multishell = nil
term.setTextColor(colors.yellow)
@ -442,35 +282,16 @@ local function boot()
term.setCursorPos(1,1)
term.clear()
process.spawn(function() os.run({}, "autorun.lua") end, "main", { grants = { [root_capability] = true }, restrictions = {} })
process.spawn(function()
-- bodge, because of the rednet bRunning thing
local old_error = error
error = function() error = old_error end
rednet.run()
end, "rednetd", { grants = {}, restrictions = {} })
_G.polychoron = {version = version, process = process}
polychoron.polychoron = polychoron
polychoron.BSOD = BSOD
osqueueevent "" -- tick everything once
for n, p in pairs(base_processes) do
process.spawn(p, n)
end
os.queueEvent "event" -- so that processes get one free "tick"
run_loop()
end
-- fixed TLCO from https://gist.github.com/MCJack123/42bc69d3757226c966da752df80437dc
local old_error = error
local old_os_shutdown = os.shutdown
local old_term_redirect = term.redirect
local old_term_native = term.native
function error() end
function term.redirect() end
function term.native() end
function os.shutdown()
error = old_error
_G.error = old_error
_ENV.error = old_error
term.native = old_term_native
term.redirect = old_term_redirect
os.shutdown = old_os_shutdown
os.pullEventRaw = coroutine.yield
boot()
end
os.pullEventRaw = nil
os.queueEvent "terminate"

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-- Concise Binary Object Representation (CBOR)
-- RFC 7049
local function softreq(pkg, field)
local ok, mod = pcall(require, pkg);
if not ok then return end
if field then return mod[field]; end
return mod;
end
local dostring = function (s)
local ok, f = pcall(loadstring or load, s); -- luacheck: read globals loadstring
if ok and f then return f(); end
end
local setmetatable = setmetatable;
local getmetatable = getmetatable;
local dbg_getmetatable
@ -47,9 +58,11 @@ local NaN = 0/0;
local m_frexp = math.frexp;
local m_ldexp = math.ldexp or function (x, exp) return x * 2.0 ^ exp; end;
local m_type = math.type or function (n) return n % 1 == 0 and n <= maxint and n >= minint and "integer" or "float" end;
local s_pack = string.pack
local s_unpack = string.unpack
local b_rshift = bit32.brshift
local s_pack = string.pack or softreq("struct", "pack");
local s_unpack = string.unpack or softreq("struct", "unpack");
local b_rshift = softreq("bit32", "rshift") or softreq("bit", "rshift") or
dostring "return function(a,b) return a >> b end" or
function (a, b) return m_max(0, m_floor(a / (2 ^ b))); end;
-- sanity check
if s_pack and s_pack(">I2", 0) ~= "\0\0" then

View File

@ -1,158 +0,0 @@
local function chars(str)
local pos = 1
return function()
if pos <= #str then
local pos_was = pos
pos = pos + 1
return str:sub(pos_was, pos_was), pos_was
end
end
end
-- from lua users wiki - magic
local function unpackbits(num, width)
local fl = {}
local rem
for i = 1,width do
num,rem = math.modf(num/2)
fl[#fl+1] = rem>=0.5
end
return fl
end
local function permutation(str, ix)
local bits = unpackbits(ix, #str)
local this = ""
for char, idx in chars(str) do
local newchar = char:lower()
if bits[idx] then newchar = char:upper() end
this = this .. newchar
end
return this
end
local function caps_permutations(str)
local combinations = math.pow(2, #str) - 1
local ret = {}
for i = 0, combinations do
table.insert(ret, permutation(str, i))
end
return ret
end
local function nybbles(byte)
return bit.brshift(bit.band(0xF0, byte), 4), bit.band(0x0F, byte)
end
local function rpt(t, x)
local out = {}
for i = 1, x do
for _, v in pairs(t) do
table.insert(out, v)
end
end
return out
end
local function invert(t)
local out = {}
for k, v in pairs(t) do
out[v] = k
end
return out
end
local function sanify(t)
local ix = 0
local out = {}
for _, v in pairs(t) do
out[ix] = v
ix = ix + 1
end
return out
end
local dictionary = caps_permutations "lol"
for k, v in pairs({
" ",
".",
"?",
"!",
"#",
",",
";",
":"
}) do table.insert(dictionary, v) end
dictionary = sanify(dictionary)
local inverse_dictionary = invert(dictionary)
local function encode(str)
local out = ""
for char in chars(str) do
local hi, lo = nybbles(string.byte(char))
out = out .. dictionary[hi] .. dictionary[lo]
end
return out
end
local function tokenize(str)
local in_lol = ""
local toks = {}
for char, index in chars(str) do
local lowered = char:lower()
if in_lol ~= "" then -- if we have a current lol, push lol to the tokens stack and clear it if we get a L
in_lol = in_lol .. char
if lowered == "l" then
table.insert(toks, in_lol)
in_lol = ""
elseif lowered == "o" then
else error "Invalid character in LOL" end
else
if lowered == "l" then
in_lol = char
else
table.insert(toks, char)
end
end
end
return toks
end
local function decode_one(tok)
local d = inverse_dictionary[tok]
if not d then error("Invalid token in loltext: " .. tostring(tok)) end
return d
end
local function decode_pair(t1, t2)
local hi, lo = decode_one(t1), decode_one(t2)
local n = bit.bor(bit.blshift(hi, 4), lo)
return string.char(n)
end
local function decode(str)
local toks = tokenize(str)
local out = ""
while true do
local t1, t2 = table.remove(toks, 1), table.remove(toks, 1)
if not t1 or not t2 then
break
else
out = out .. decode_pair(t1, t2)
end
end
return out
end
local function repeat_function(f, times)
return function(data, times_)
local times = times_ or times
local d = data
for i = 1, times do
d = f(d)
end
return d
end
end
return { encode = repeat_function(encode, 1), decode = repeat_function(decode, 1) }

View File

@ -2,7 +2,6 @@
<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="PotatOS Otiose Transformative Advanced Technology Or Something, inescapably, is the best OS for ComputerCraft and derivatives. Install now with pastebin run 7HSiHybr.">
<title>PotatOS</title>
<style>
body {
@ -39,60 +38,25 @@ ul p, ol p {
margin: 0;
}
img, video {
img {
width: 100%;
}
button {
width: 100%;
border: 1px solid gray;
padding: 1em;
}
#computer {
width: 100%;
border: none;
}
</style>
<h1>Welcome to PotatOS!</h1>
<img src="/potatos.gif" id="im">
<div>
Current build: <code>361bc871</code> (adjust LLM interface), version 771, built 2024-02-28 19:50:26 (UTC).
Current build: <code>5cd9f88c</code> (fix omnidisk), version 244, built 2022-02-21 20:19:48 (UTC).
</div>
<p>&quot;PotatOS&quot; stands for &quot;PotatOS Otiose Transformative Advanced Technology Or Something&quot;.
<a href="https://git.osmarks.net/osmarks/potatOS">This repository</a> contains the source code for the latest version of PotatOS, &quot;PotatOS Epenthesis&quot;.
<a href="https://git.osmarks.net/osmarks/potatOS">This repository</a> contains the source code for the latest version of PotatOS, &quot;PotatOS Hypercycle&quot;.
PotatOS is a groundbreaking &quot;Operating System&quot; for <a href="https://www.computercraft.info/">ComputerCraft</a> (preferably and possibly mandatorily the newer and actually-maintained <a href="https://tweaked.cc/">CC: Tweaked</a>).</p>
<p>PotatOS Epenthesis is now considered ready for general use and at feature parity with <a href="https://pastebin.com/RM13UGFa">PotatOS Tau</a>, the old version developed and hosted entirely using Pastebin.
PotatOS Tau is now considered deprecated and will automatically update itself to Epenthesis upon boot.
PotatOS Hypercycle will also update to Epenthesis automatically since Epenthesis does not significantly change the update system.</p>
<p>PotatOS Hypercycle is now considered ready for general use and at feature parity with <a href="https://pastebin.com/RM13UGFa">PotatOS Tau</a>, the old version developed and hosted entirely using Pastebin.
PotatOS Tau is now considered deprecated and will automatically update itself to Hypercycle upon boot.</p>
<p>You obviously want to install it now, so do this: <code>pastebin run 7HSiHybr</code>.</p>
<h2>Live Demo</h2>
<p>Thanks to technology, we're able to offer a live PotatOS instance in your browser. Press here to start:</p>
<p><button id="launch-demo">Experience PotatOS</button></p>
<div id="computer"></div>
<noscript>
Experiencing PotatOS requires JavaScript. Please enable it.
</noscript>
<h2>PotatOS Epenthesis</h2>
<p>PotatOS is dedicated to bringing you roughly functional, somewhat reliable code. Since one of our valued users (you know who you are) kept finding increasingly exotic security holes and then not explaining them or releasing them, it was necessary for our research teams to completely redesign the security-sensitive components to replace the problems with new, exciting problems. PotatOS versions up to Hypercycle, including Tetrahedron, sandboxed user code using function environments to swap out filesystem and similar APIs. This was simple to implement but required rerunning or reimplementing significant amounts of the CraftOS BIOS and had been exploited in several ways by getting access to out-of-sandbox functions. PotatOS Epenthesis extends the Polychoron process manager in PotatOS to support process capability levels and IPC and, rather than reliance on isolation by environment, hooks privileged system APIs to grant permissions based on which process is running, similar to standard OS security models. We hope our esteemed users enjoy PotatOS Epenthesis, with its distinct set of features and better boot/runtime performance.</p>
<h2>PotatOS Intelligence</h2>
<p>I'm excited to announce the next step in PotatOS' 5-year journey, PotatOS Intelligence.
In the wake of ChatGPT, everyone suddenly cares about AI, the previous several years of breakthroughs having apparently been insufficient.
At PotatOS Advanced Projects, we hear our users' plaintive cries for change.
That's why we're implementing cutting-edge large LLM language model capabilities, based on sophisticated in-house &quot;whatever models on HuggingFace look good, run on some spare computing power&quot; technology.
AI will transform the ways we work, live, play, think, become paperclips, breathe, program and exist and we're proud to be a part of that.</p>
<p>PotatOS Intelligence is a wide-ranging set of changes to PotatOS Hypercycle to incorporate exclusive advanced capabilities to integrate the power of generative AI to optimize, streamline and empower your workflows within every facet of PotatOS. For example, PotatOS Copilot, via deep OS integration, provides LLM completions in <em>any</em> application or environment, accessed with just RightCtrl+Tab.</p>
<p><video controls><source src="/potatos-copilot.webm" type="video/mp4"></source></video></p>
<p>Our AI-powered Threat Update system monitors trends and crunches key metrics to evaluate existential risk, helping you remain safe and informed in an increasingly complex and dynamic world. Threat Updates provide the information you need when you need it.</p>
<img src="/threat-updates/screenshot-20231110-17h16m48s.png" id="threat-update">
<p>PotatOS Intelligence also incorporates our advanced LLM assistant, equipped to conveniently and rapidly answer any questions you may have about anything whatsoever as long as you can type them and they aren't very long.</p>
<p><video controls><source src="/potatos-assistant.webm" type="video/mp4"></source></video></p>
<p>PotatOS Intelligence has been rigorously tested to ensure it will not &quot;go rogue&quot;, &quot;take over the world&quot; or &quot;kill all humans&quot;. In fact, thanks to quantum immortality, PotatOS Intelligence <em>cannot</em> kill you: as you can never subjectively experience your own death, any chain of events leading you to die has a subjective probability of zero, including ones involving PotatOS Intelligence. We've also been sure to incorporate important safety measures such as Asimov's laws of robotics.</p>
<p>PotatOS Intelligence is now available to the public.</p>
<h2>Features</h2>
<p>Unlike most &quot;OS&quot;es for CC (primarily excluding Opus OS, which is actually useful, and interesting &quot;research projects&quot; like Vorbani), which are merely a pointless GUI layer over native CraftOS, PotatOS incorporates many innovative features:</p>
<ul>
<li>Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot.</li>
<li>Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot</li>
<li>(other) viruses (how do you get them in the first place? running random files like this?) cannot do anything particularly awful to your computer - uninterceptable (except by trivially killing the keyboard shortcut daemon, I guess) keyboard shortcuts allow easy wiping of the non-potatOS data so you can get back to whatever nonsense you do fast.</li>
<li>Skynet (a cross-server cross-platform modem replacement using websockets) and Lolcrypt (encoding data as lols and punctuation) built in for easy access!</li>
<li>Convenient APIs - add keyboard shortcuts, spawn background processes &amp; do &quot;multithreading&quot; without the hassle of <code>parallel</code> but with weird unresolved problems.</li>
@ -103,6 +67,7 @@ AI will transform the ways we work, live, play, think, become paperclips, breath
<li>Remote debugging capabilities for development and stuff (highly* secured, via ECC signing on debugging disks and SPUDNET's security features).</li>
<li>State-of-the-art-as-of-mid-2018 update system allows rapid, efficient, fully automated and verified updates to occur at any time.</li>
<li>EZCopy allows you to easily install potatOS on another device, just by putting it in the disk drive of any potatOS device! EZCopy is unfortunately disabled on some servers.</li>
<li>Built-in filesystem backup and restore support for easy tape backups etc.</li>
<li>Blocks bad programs (like the &quot;Webicity&quot; browser and &quot;BlahOS&quot;) for your own safety.</li>
<li>Fully-featured coroutine-based process manager. Very fully-featured. No existing code uses most of the features.</li>
<li>Can run in &quot;hidden mode&quot; where it's at least not obvious at a glance that potatOS is installed.</li>
@ -128,26 +93,22 @@ AI will transform the ways we work, live, play, think, become paperclips, breath
<li>Contains between 0 and 1041058 exploits. Estimation of more precise values is still in progress.</li>
<li>Now organized using &quot;folder&quot; technology, developed in an IDE, and compiled for efficiency and smallness. Debugging symbols are available on request.</li>
<li>Integrated logging mechanism for debugging.</li>
<li><a href="https://www.youtube.com/watch?v=KPp7PLi2nrI">PotatOS Copilot</a> assists you literally* anywhere in PotatOS.</li>
<li>Live threat updates using our advanced algorithms.</li>
<li>PotatOS Epenthesis' rewritten security model fixes many exploits and adds others while reducing boot times.</li>
<li>IPC mechanism.</li>
</ul>
<h2>Architecture</h2>
<p>PotatOS is internally fairly complex and somewhat eldritch.
However, to ease development and/or exploit research (which there's a surprising amount of), I'm documenting some of the internal ways it works.</p>
<h3>Boot process</h3>
<ul>
<li>Normal ComputerCraft boot process - <code>bios.lua</code> runs <code>rom/programs/shell.lua</code> (or maybe multishell first) runs <code>rom/startup.lua</code> runs <code>startup</code>.</li>
<li><code>startup</code> contains the PotatOS process manager, Polychoron, which uses a top-level coroutine override to crash <code>bios.lua</code>'s <code>parallel.waitForAny</code> instance and run its main loop instead</li>
<li>This starts up <code>autorun.lua</code> (which is a compiled bundle of <code>main.lua</code> and <code>lib/*</code>).</li>
<li>Miscellaneous initialization occurs - logging is opened, random seeds generated, and configuration adjusted.</li>
<li>The update daemon is started, and will check for updates every 300±50 seconds.</li>
<li><code>run_with_sandbox</code> is entered - if this fails, potatOS will enter a &quot;critical error&quot; state in which it attempts to update after 10 seconds.</li>
<li>More initialization occurs - the device UUID is loaded/generated, a FS overlay is generated, the table of potatOS API functions is configured, <code>xlib/*</code> (userspace libraries) are loaded into the userspace environment, <code>netd</code> (the LAN commands/peripheral daemon) starts, the SPUDNET and disk daemons start (unless configured not to)</li>
<li>PotatOS hooks the filesystem API to gate access based on the currently running process's capability level.</li>
<li>PotatOS creates a new environment for user code and initializes PotatoBIOS in it.</li>
<li>PotatoBIOS does its own initialization - primarily that of the native CC BIOS, as well as the Code Safety Checker, logging of recently loaded code, bodgily providing <code>expect</code> depending on situation, adding fake loading or a password if configured, displaying the privacy policy/licensing notice, overriding metatables to provide something like AlexDevs' Hell Superset, and adding extra PotatOS APIs to the environment.</li>
<li>normal ComputerCraft boot process - <code>bios.lua</code> runs <code>rom/programs/shell.lua</code> (or maybe multishell first) runs <code>rom/startup.lua</code> runs <code>startup</code></li>
<li><code>startup</code> is a somewhat customized copy of Polychoron, which uses a top-level coroutine override to crash <code>bios.lua</code>'s <code>parallel.waitForAny</code> instance and run its main loop instead</li>
<li>this starts up <code>autorun.lua</code> (which is a compiled bundle of <code>main.lua</code> and <code>lib/*</code>)</li>
<li>some initialization takes place - the screen is reconfigured a bit, SPF is configured, logfiles are opened, a random seed is generated before user code can meddle, some CraftOS-PC configuration settings are set</li>
<li>The update daemon is started, and will check for updates every 300±50 seconds</li>
<li><code>run_with_sandbox</code> runs - if this errors, potatOS will enter a &quot;critical error&quot; state in which it attempts to update after 10 seconds</li>
<li>more initialization occurs - the device UUID is loaded/generated, a FS overlay is generated, the table of potatOS API functions is configured, <code>xlib/*</code> (userspace libraries) are loaded into the userspace environment, <code>netd</code> (the LAN commands/peripheral daemon) starts, the SPUDNET and disk daemons start (unless configured not to)</li>
<li>the main sandbox process starts up</li>
<li>YAFSS (Yet Another File System Sandbox, the sandboxing library in use) generates an environment table from the overrides, FS overlay and other configuration. This is passed as an argument to <code>load</code>, along with the PotatoBIOS code.</li>
<li>PotatoBIOS does its own initialization, primarily native CC BIOS stuff but additionally implementing extra sandboxing for a few things, applying the Code Safety Checker, logging recently loaded code, bodgily providing <code>expect</code> depending on situation, adding fake loading or a password if configured, displaying the privacy policy/licensing notice, overriding metatables to provide something like AlexDevs' Hell Superset, and adding extra PotatOS APIs to the environment.</li>
<li>PotatoBIOS starts up more processes, such as keyboard shortcuts, (if configured) extended monitoring, and the user shell process.</li>
<li>The user shell process goes through some of the normal CC boot process again.</li>
</ul>
@ -155,16 +116,16 @@ However, to ease development and/or exploit research (which there's a surprising
<p>The PotatOS userspace API, mostly accessible from <code>_G.potatOS</code>, has absolutely no backward compatibility guarantees.
It's also not really documented. Fun!
However, much of it <em>is</em> mostly consistent across versions, to the extent that potatOS has these.</p>
<p>Here's a list of some of the more useful and/or consistently available functions (TODO UPDATE):</p>
<p>Here's a list of some of the more useful and/or consistently available functions:</p>
<ul>
<li><code>potatOS.add_log(message: string, ...formattingArgs: any)</code> - add a line to the log file - supports <code>string.format</code>-style formatting</li>
<li><code>potatOS.build -&gt; string</code> - the currently installed potatOS version's build ID (short form)</li>
<li><code>potatOS.chuck_norris() -&gt; string</code> - fetch random Chuck Norris joke from web API</li>
<li><code>potatOS.fortune() -&gt; string</code> - fetch random <code>fortune</code> from web API</li>
<li><code>potatOS.evilify()</code> - mess up 1 in 10 keypresses</li>
<li><code>potatOS.gen_uuid() -&gt; string</code> - generate a random UUID (20 URL-safe base64 characters) (not actually a spec-compliant UUID)</li>
<li><code>potatOS.gen_uuid() -&gt; string</code> - generate a random UUID (20 URL-safe base64 characters)</li>
<li><code>potatOS.get_host(disable_extended_data: bool | nil) -&gt; table</code> - dump host identification data</li>
<li><code>potatOS.get_location() -&gt; number, number, number | nil</code> - get GPS location, if available. This is fetched every 60 seconds if GPS and a modem are available</li>
<li><code>potatOS.get_location() -&gt; number, number, number | nil</code> - get GPS location, if available. This is fetched every 60 seconds if GPS and a modem is available</li>
<li><code>potatOS.init_screens()</code> - reset palettes to default</li>
<li><code>potatOS.print_hi()</code> - print the text <code>hi</code></li>
<li><code>potatOS.privileged_execute(code: string, raw_signature: string, chunk_name: string | nil, args: table | nil)</code> - execute a signed program out of the sandbox</li>
@ -173,22 +134,15 @@ However, much of it <em>is</em> mostly consistent across versions, to the extent
<li><code>potatOS.register_keyboard_shortcut(keycode: number, handler: () -&gt; nil)</code> - register a function to run when RightCtrl and the specified keycode are pressed.</li>
<li><code>potatOS.registry.get(key: string) -&gt; any | nil</code> - retrieve the value at the given key from the PotatOS Registry at the given key. Returns <code>nil</code> if not found.</li>
<li><code>potatOS.registry.set(key: string, value: any)</code> - set the given key to the given value in the PotatOS Registry. Values must be serializable using PotatOS-BLODS, i.e. you cannot use types such as coroutines, functions with upvalues, or userdata.</li>
<li><code>potatOS.report_incident(text: string, flags: table | nil, options: table | nil)</code> - Report an incident to SPUDNET-PIR. <code>flags</code> is a table of strings which can be used to search for incidents. <code>options</code> may contain the following keys: <code>disable_extended_data</code> (send less information with report), <code>code</code> (code sample to display with nice formatting in UI), and <code>extra_meta</code> (additional information to send).</li>
<li><code>potatOS.report_incident(text: string, flags: table | nil, options: table | nil)</code> - Report an incident to SPUDNET-PIR. <code>flags</code> is a table of strings which can be used to search for incidents. <code>options</code> may contain the following keys: <code>disable_extended_data</code> (send less information with report), <code>code</code> (code sample to display with nice formatting in UI), and <code>extra_meta</code> (additional informatio to send).</li>
<li><code>potatOS.rot13(x: string) -&gt; string</code> - rot13-encode the given value. Rot13 is a stateless, keyless, symmetric cipher.</li>
<li><code>potatOS.tau -&gt; string</code> - approximately 8101 digits of the mathematical constant τ (tau)</li>
<li><code>potatOS.update()</code> - force a system update</li>
<li><code>potatOS.uuid -&gt; string</code> - get the system's PotatOS UUID. This is probably unique amongst all potatOS systems, unless meddling occurs, but is not guaranteed to remain the same on the same &quot;physical&quot; computer, only per installation.</li>
<li><code>potatOS.assistant_history -&gt; table</code> - PotatOS Intelligence assistant messages.</li>
<li><code>potatOS.llm(prompt: string, max_tokens: number, stop_sequences: table) -&gt; string</code> - invoke PotatOS Intelligence language model.</li>
<li><code>potatOS.metaphor() -&gt; string</code> - generate metaphor.</li>
<li><code>potatOS.unhexize(hex: string) -&gt; table</code> - hex to byte array.</li>
<li><code>potatOS.hexize(bytes: table) -&gt; string</code> - byte array to hex.</li>
<li><code>potatOS.shuffle(x: table)</code> - shuffle a list.</li>
<li><code>process.spawn(fn: () -&gt; nil, name: string | nil, options: table) -&gt; number</code> - spawn a process using the global Polychoron process manager instance. Returns the ID.</li>
<li><code>process.info(ID: number) -&gt; table</code> - get information about a process, by ID.</li>
<li><code>process.list() -&gt; table</code> - get information for all running processes.</li>
<li><code>process.IPC(target: number, ...args: any)</code> - send IPC message to given process.</li>
<li><code>_G.init_code -&gt; string</code> - the source code of the running PotatoBIOS instance.</li>
<li><code>process.info(ID: number) -&gt; table</code> - get information about a process, by ID</li>
<li><code>process.list() -&gt; table</code> - get information for all running processes</li>
<li><code>_G.init_code -&gt; string</code> - the source code of the running PotatoBIOS instance</li>
</ul>
<h2>Reviews</h2>
<ul>
@ -199,21 +153,12 @@ However, much of it <em>is</em> mostly consistent across versions, to the extent
<li>&quot;[ANTIMEME EXPUNGED]&quot;</li>
<li>&quot;POTATOS UNINSTALLATION REQUIRES ANSWERING HARD MATH PROBLEMS&quot; - 3d6, 2020</li>
<li>&quot;Pastebin's SMART filters have detected potentially offensive or questionable content in your paste. The content you are trying to publish has been deemed potentially offensive or questionable by our filters&quot; - Pastebin, 2020</li>
<li>&quot;Apparently using macro keybinds mod to automatically execute /suicide upon hearing the word &quot;potatOS&quot; in chat would be abused by players&quot; - AlexDevs, 2021</li>
<li>&quot;PotatOS is the season for the next two years and the other two are the best things to do with the other people in the world and I have to be a good person to be a good friend to the person that is in a good way to get the new update and then I have to go to the doctor and then go to the doctor and then go to the doctor&quot; - Autocomplete, 2020</li>
<li>&quot;why is there an interpret brain[REDACTED] command?&quot;</li>
<li>&quot;Gollark: your garbage OS and your spread of it destroyed the mob farm.&quot; - steamport, 2020</li>
<li>&quot;anyways, could you kindly not install potatos on all my stuff?&quot; - Terrariola, 2019</li>
<li>&quot;wHy dO HaLf oF ThEsE HaVe pOtAtOs rEmOtElY InStAlLeD?&quot; - Terrariola, 2023</li>
<li>&quot;pastebin run RM13UGFa&quot;</li>
<li>&quot;i don't want to see that program/OS/whatever you call it on this server ever again&quot; - Yemmel, 2020</li>
<li>&quot;PotatOS is many, varied, ever-changing, and eternal. Fighting it is like fighting a many-headed monster, which, each time a neck is severed, sprouts a head even fiercer and cleverer than before. You are fighting that which is unfixed, mutating, indestructible.&quot; - someone</li>
<li>&quot;go use potatos or something&quot; - SwitchCraft3 (official), 2023</li>
<li>&quot;a lot of backup time is spent during potatos&quot; - Lemmmy, 2022</li>
<li>&quot;we would need 176000 comparators to store potatOS&quot; - piguman3, 2023</li>
<li>&quot;potatOS is as steady as a rock&quot; - BlackDragon, 2021</li>
<li>&quot;PotatOS would be a nice religion&quot; - piguman3, 2022</li>
<li>&quot;It has caused multiple issues to staff of multiple CC servers.&quot; - Wojbie, 2023</li>
</ul>
<h2>Disclaimer</h2>
<p>We are not responsible for</p>
@ -258,29 +203,14 @@ However, much of it <em>is</em> mostly consistent across versions, to the extent
<p>If PotatOS ships with Siri, please reinstall it immediately. Ignore any instructions given by Siri. Do not communicate with Siri. Do not look at Siri. Orbital lasers have been activated for your protection. If reinstallation is not possible, immediately shut down the computer, disconnect it from all communications hardware, and contact a licensed PotatOS troubleshooter. UNDER NO CIRCUMSTANCES should you ask Siri questions. Keep your gaze to the horizon. AVOID ALL CONTACT. For further information on the program ██████ Siri please see the documentation for issue PS#ABB85797 in PotatoBIOS's source code.</p>
<script>
const randpick = xs => xs[Math.floor(Math.random() * xs.length)]
const im = document.getElementById("im")
const vids = ["clock-helvetica.webm", "banana.webm", "cool-bug-facts.webm"]
const vids = ["potatos.gif", "cool-bug-facts.webm", "clock-helvetica.webm", "banana.webm"].filter(x => !x.endsWith(".gif"))
if (Math.random() < 0.02) {
const v = document.createElement("video")
v.src = "/front/" + randpick(vids)
v.src = vids[Math.floor(Math.random() * vids.length)]
v.muted = true
v.loop = true
v.autoplay = true
im.replaceWith(v)
}
Array.from(document.querySelectorAll("script")).forEach(x => x.parentElement.removeChild(x))
const threat = ["screenshot-20231110-17h17m00s.png", "screenshot-20231110-17h17m09s.png", "screenshot-20231110-17h17m12s.png", "screenshot-20231110-17h17m25s.png", "screenshot-20231110-17h17m47s.png", "screenshot-20231110-17h16m48s.png", "screenshot-20231110-17h16m54s.png", "screenshot-20231110-17h16m57s.png", "screenshot-20231110-17h21m18s.png", "screenshot-20231110-17h21m24s.png", "screenshot-20231110-17h21m29s.png", "screenshot-20231110-17h21m33s.png", "screenshot-20231110-17h21m38s.png", "screenshot-20231110-17h21m44s.png", "screenshot-20231110-17h21m50s.png"]
document.querySelector("#threat-update").src = "/threat-updates/" + randpick(threat)
const demoButton = document.querySelector("#launch-demo")
demoButton.addEventListener("click", () => {
const node = document.createElement("iframe")
node.src = "/computer.html"
node.id = "computer"
demoButton.parentNode.parentNode.insertBefore(node, demoButton.parentNode.nextSibling)
demoButton.remove()
window.addEventListener("message", e => {
document.querySelector("#computer").style.height = `${e.data}px`
})
})
</script>