Files
mesh-catalog/modules/rofi/cmd/rofi-tools/main.go
T
jschoubben f5cbfd850c
mesh/merge-gate pass: builds rofi → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
rofi: write the hidden prompt from names and say the controller asks, never with the caller's words
The review of 2026-10-09 (M4): the prompt and its message were the caller's free text, so whoever could
ask the verb could put any words before the operator. The verb now takes the module, the secret and the
machine, checked as names, and writes the prompt here, saying the mesh's controller asks — which the bus
makes true, denying the verb to every other principal (mesh-controller #156). prompt and message are refused.
2026-10-09 17:08:17 +02:00

127 lines
5.6 KiB
Go

// rofi's Go tools bundle (novox/hq ADR 0188, ADR 0193, ADR 0208): its implementation of
// node-launcher's verb `menu` — the dmenu-compatible command as a tool — and its own tools, served by
// the node's runtime as the operator account.
package main
import (
"fmt"
"os"
stdio "git.novox.be/novox/mesh-sdk/go"
)
func main() {
if err := stdio.Serve("", tools()); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func tools() []stdio.Tool {
return []stdio.Tool{
{
Name: "node-launcher.menu",
Description: fmt.Sprintf("Show a list in the operator's session and answer the line they choose: "+
"the launcher's dmenu-compatible command as a tool. Answers chosen and its index, or cancelled "+
"when they closed it or did not answer within timeout_seconds (default %d, at most %d).", menuDefault, menuMost),
Input: map[string]any{
"type": "object",
"properties": map[string]any{
"items": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "the choices, one line each"},
"prompt": map[string]any{"type": "string", "description": "the prompt (default: Choose)"},
"message": map[string]any{"type": "string", "description": "a line of explanation above the list"},
"allow_custom": map[string]any{"type": "boolean", "description": "accept a typed answer that is not in the list (default false)"},
"timeout_seconds": map[string]any{"type": "integer", "description": fmt.Sprintf("give up after this long (default %d, at most %d)", menuDefault, menuMost)},
},
"required": []string{"items"},
},
Run: func(args map[string]any) (any, error) {
q, err := menuQuestion(args)
if err != nil {
return nil, err
}
return Menu(q)
},
},
{
Name: "node-launcher.secret",
Description: fmt.Sprintf("Ask the operator for a value in a prompt that shows nothing of what is typed, "+
"and answer it sealed to the key seal_to names — never in the clear (novox/hq ADR 0259 §10): how the "+
"mesh takes a module's own secret at the desk. The prompt is written here from the names, and says the "+
"mesh's controller asks: the bus lets nobody else ask it. Answers sealed, or cancelled when the prompt was "+
"dismissed, answered empty or not answered within timeout_seconds (default and at most %d).", menuMost),
Input: map[string]any{
"type": "object",
"properties": map[string]any{
"module": map[string]any{"type": "string", "description": "the module whose own secret is asked for"},
"secret": map[string]any{"type": "string", "description": "the own secret's name"},
"node": map[string]any{"type": "string", "description": "the machine the module runs on"},
"seal_to": map[string]any{"type": "string", "description": "the asker's public sealing key, base64: the answer is sealed to it"},
"timeout_seconds": map[string]any{"type": "integer", "description": fmt.Sprintf("give up after this long (at most %d)", menuMost)},
},
"required": []string{"module", "secret", "node", "seal_to"},
},
Run: func(args map[string]any) (any, error) {
q, err := secretQuestion(args)
if err != nil {
return nil, err
}
return Secret(q)
},
},
{
Name: "rofi_applications",
Description: "The desktop applications the launcher offers on this machine, from every desktop entry " +
"directory in the order the launcher reads them (the account's first): id, name, what it runs, " +
"categories and the file. Hidden entries are left out unless asked for.",
Input: map[string]any{
"query": map[string]any{"type": "string", "description": "only entries whose name, generic name, keywords or command contain this (any case)"},
"show_hidden": map[string]any{"type": "boolean", "description": "include entries marked NoDisplay or Hidden (default false)"},
"limit": map[string]any{"type": "integer", "description": "at most this many (default 200, at most 2000)"},
},
Run: func(args map[string]any) (any, error) {
query, err := text(args, "query", false)
if err != nil {
return nil, err
}
hidden, err := flag(args, "show_hidden", false)
if err != nil {
return nil, err
}
limit, err := whole(args, "limit", 200, 1, 2000)
if err != nil {
return nil, err
}
return Applications(applicationDirs(), query, hidden, limit), nil
},
},
{
Name: "rofi_themes",
Description: "The launcher's themes on this machine — the mesh's, the operator's own and the " +
"distribution's — each with its file, and which one the configuration uses.",
Run: func(map[string]any) (any, error) { return Themes(themeDirs(), configFile()), nil },
},
{
Name: "rofi_run",
Description: "Start a program in the operator's session, as the launcher would: a desktop entry by " +
"its id (firefox.desktop), or a command as its words. It runs under the account's own service " +
"manager, so it outlives this call; answers the unit it runs as and what it ran.",
Input: map[string]any{
"application": map[string]any{"type": "string", "description": "a desktop entry id, as rofi_applications answers it"},
"command": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "a program and its arguments, one word each"},
},
Run: func(args map[string]any) (any, error) {
app, err := text(args, "application", false)
if err != nil {
return nil, err
}
command, err := texts(args, "command")
if err != nil {
return nil, err
}
return Run(app, command)
},
},
}
}