Skip to content

Commit

Permalink
Merge pull request #83 from uqbar-dao/da/scripts
Browse files Browse the repository at this point in the history
scripts
  • Loading branch information
edgaruncentered authored Jan 25, 2024
2 parents 193402a + fda1703 commit 8e33563
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
- [Publishing a Website or Web App](./cookbook/publish_to_web.md)
- [Simple File Transfer Guide](./cookbook/file_transfer.md)
- [Intro to Web UI with File Transfer](./cookbook/file_transfer_ui.md)
- [Writing and Running Scripts](./cookbook/writing_scripts.md)

# API Reference

Expand Down
45 changes: 14 additions & 31 deletions src/apis/terminal.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,27 @@

The Kinode terminal is broken up into two segments: a Wasm app, called `terminal:terminal:sys`, and a runtime module called `terminal:distro:sys`.
The Wasm app is the central area where terminal logic and authority live.
It parses `Requests` by attempting to read the `body` field as a UTF-8 string, then parsing that string into various commands (usually denoted by a `/`) to perform.
It parses `Requests` by attempting to read the `body` field as a UTF-8 string, then parsing that string into various commands to perform.
The runtime module exists in order to actually use this app from the terminal which is launched by starting Kinode OS.
It manages the raw input and presents an interface with features such as command history, text manipulation, and shortcuts.

To "use" the terminal as an API, one must simply send a `Request` to the `terminal:terminal:sys` module.
This is a powerful capability, as it allows the process to send a `Request` to the terminal and have it be parsed and executed.
To "use" the terminal as an API, one simply needs the capability to message `terminal:terminal:sys`.
This is a powerful capability, equivalent to giving an application `root` authority over your node.
For this reason, users are unlikely to grant direct terminal access to most apps.

If one does have the capability to send `Request`s to the terminal, they can use the following commands:
If one does have the capability to send `Request`s to the terminal, they can execute commands like so:
`script_name:package_name:publisher_name <ARGS>`

```
/hi <node_id> <message>
```
Send a raw network message to another node.
For example, the `hi` script, which pings another node's terminal with a message, can be called like so: `hi:terminal:sys default-router-1.os what's up?`.
In this case, the arguments are both `default-router-1.os` and the message `what's up?`.

```
/app <address>
/a <address>
```
Set the terminal to send all subsequent messages to a certain process.
Some commonly used scripts have shorthand aliases because they are invoked so frequently.
For example, `hi:terminal:sys` can be shortened to just `hi` as in: `hi default-router-1.os what's up?`.

The other most commonly used script is `m:terminal:sys`, or just `m` - which stands for `Message`. `m` let's you send a request to any node or application like so:
```bash
m john.os@proc:pkg:pub {"foo":"bar"}
```
/app clear
/a clear
```
Remove the set process.

```
/message <address> <request_body>
/m <address> <request_body>
```
Send a `Request` with the given body to the given address.
If `/app` or `/a` has been used to set a process, the address parameter here must be omitted, and the set one will be used instead.

The plaintext format of an `Address` looks like <node_id>`@`<process_id>.
`ProcessId` is a triple of the form <process_name>`:`<package_name>`:`<publisher_name>.

Example address:
```
some_user.os@process_one:my_cool_software:my_username.os
```
Note that if your process has the ability to message the `terminal` app, then that process can call any script - of which there may be many on a machine, so we cannot possibly write all of them down in this document.
However, they will all have this standard calling convention of `<script-name> <ARGS>`.
73 changes: 73 additions & 0 deletions src/cookbook/writing_scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Scripts
Scripts are just processes.
They are written almost exactly like applications, with a few key differences:
- Scripts always terminate, while apps may run indefinitely.
When writing a script, you cannot control the `OnExit` behavior like you can with an application
- Scripts are called with an initial set of arguments (passed in via the `terminal`)
- Scripts are registered in the `scripts.json` file instead of the `manifest.json` file

## Writing a Script
Let's look at the simplest possible script: `echo`, which takes in an argument, and prints it out again:
```rust
use kinode_process_lib::{await_next_request_body, call_init, println, Address};

wit_bindgen::generate!({
path: "wit",
world: "process",
exports: {
world: Component,
},
});

call_init!(init);

fn init(_our: Address) {
let Ok(args) = await_next_request_body() else {
println!("echo: failed to get args, aborting");
return;
};

println!(
"{}",
String::from_utf8(args).unwrap_or("echo: error".into())
);
}
```
From writing applications, this should look very familiar - the imports, `wit_bindge::generate!`, `call_init!`, `init(our: Address)`, etc. are all exactly the same.
The first unique thing about scripts is that we will have no `loop` where we `await_message`.
Instead, our initial arguments will come from a single message from the terminal - which we get by calling `await_next_message_body()`.
Next, all we do is `String`ify the message body, and print it out.

Arbitrary logic can be put below `await_next_message_body` - just like an app, you can fire-off a number of requests, choose to await their responses, handle errors, etc. just like normal.

## Publishing a Script
Unlike processes accociated with a long-running application, which will be put into the `manifest.json`, scripts must be registered in a separate `scripts.json` file.
While very similar, there are a few important differences; let's take a look at an example that could live in your packages `pkg/scripts.json` file:
```json
{
"echo.wasm": {
"root": false,
"public": false,
"requestNetworking": false,
"requestCapabilities": [],
"grantCapabilities": []
}
}
```
This `scripts.json` file corresponds to a package which publishes a single script, `echo`, which doesn't request `root` capabilities, or any capabilities for that matter.
The keys of this object are the process paths inside of the `pkg/` folder.
The name of the script will be the file path, with `.wasm` taken off.
The object that `echo.wasm` points to is very similar to `manifest.json`, with a few things removed, and `root` has been added:
- `root` means that all the capabilities held by the `terminal:terminal:sys` are passed to this script. This is rarely needed.
- `public`: same as `manfiest.json` - corresponds to whether or not other processes can message `echo.wasm` without the messsaging cap
- `requestNetworking`: same as `manfiest.json` - corresponds to whether or not this script will need to send messaages over the network
- `requestCapabilities`: same as `manifest.json` - a list of capabilities that will be granted to this script on startup (NOTE if you have `root`, there is no reason to populate `requestCapabilities` as well)
- `grantCapabilities`: same as `manifest.json` - a list of messaging caps to `echo.wasm` to be given to other processes on startup
As long as you have a `scripts.json` file, your scripts will be callable from the terminal when someone else downloads your package

## Calling a Script
Calling a script is very easy, simply type in the terminal `my_script:my_package:publisher <ARGS>` in the terminal.
For instance, the `echo` script is published as part of `terminal:sys`, so you can call
```bash
echo:terminal:sys Hello World!
```
5 changes: 3 additions & 2 deletions src/process-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ For example, the filesystem has read/write capabilities that determine whether y

## Startup Capabilities with `manifest.json`

When developing a process, the first encounter you will have with capabilities is with the `manifest.json` file, where capabilities are directly granted to a process on startup.
Upon install, the package manager (also referred to as "app store") surfaces these requested capabilities to the user, who can then choose to grant them or not.Here is a `manfiest.json` example for the `chess` app:
When developing an application, `manifest.json` will be your first encounter with capabilties. With this file, capabilities are directly granted to a process on startup.
Upon install, the package manager (also referred to as "app store") surfaces these requested capabilities to the user, who can then choose to grant them or not.
Here is a `manfiest.json` example for the `chess` app:
```json
[
{
Expand Down

0 comments on commit 8e33563

Please sign in to comment.