Add opt-in Tentacle protocol 1.1, explicit remote grants, lifecycle IPC and expiring core leases in both SDKs. Revoke access before update preparation, including failed updates. Add transient secret entries, shared Braille cell export and isolated Xorg recovery experiments with focused tests and documented limitations. Production consent, desktop services and live interoperability remain pending.
93 lines
3.0 KiB
Go
93 lines
3.0 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// A host diagnostic tool for external Tentacles, independent of the running reader.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/tentacle"
|
|
)
|
|
|
|
func main() {
|
|
if err := run(); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func run() (runError error) {
|
|
options := flag.NewFlagSet("host", flag.ContinueOnError)
|
|
cgroupParent := options.String("cgroup-parent", "", "delegated cgroup v2 parent for resource containment")
|
|
presentation := options.Bool("presentation", false, "grant command speech/braille result capability (prints JSON only)")
|
|
interactive := options.Bool("interaction", false, "grant internal form/clipboard/focused-item capability (prints initial JSON only)")
|
|
readerSettings := options.Bool("reader-settings", false, "grant declarative reader settings capability (prints initial JSON only; does not modify a reader)")
|
|
sessionEvents := options.Bool("session-events", false, "negotiate protocol 1.1 lifecycle transport without desktop services")
|
|
remoteGrants := options.String("remote-grants", "", "comma-separated install grants for protocol 1.1; grants do not issue session consent")
|
|
command := options.String("command", "echo", "registered command to invoke")
|
|
if err := options.Parse(os.Args[1:]); err != nil {
|
|
return err
|
|
}
|
|
if options.NArg() < 1 {
|
|
return fmt.Errorf("usage: host [-cgroup-parent /delegated/parent] /absolute/program [arguments...]")
|
|
}
|
|
var manager *tentacle.Manager
|
|
var err error
|
|
if *cgroupParent == "" {
|
|
manager, err = tentacle.NewManager(tentacle.DefaultLimits(), nil)
|
|
} else {
|
|
manager, err = tentacle.NewContainedManager(tentacle.DefaultLimits(), nil, *cgroupParent, tentacle.DefaultResourceLimits())
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
runError = errors.Join(runError, manager.Shutdown(ctx))
|
|
}()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
var grants []string
|
|
if *remoteGrants != "" {
|
|
grants = strings.Split(*remoteGrants, ",")
|
|
}
|
|
session, err := manager.Start(ctx, tentacle.Spec{SessionEvents: *sessionEvents, RemoteGrants: grants, ID: "local-example", Program: options.Args(), Presentation: *presentation, Interaction: *interactive, ReaderSettings: *readerSettings})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
select {
|
|
case <-session.Ready():
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
if state, err := session.Status(); state != tentacle.Ready {
|
|
return fmt.Errorf("startup %s: %v", state, err)
|
|
}
|
|
if err := session.Activate(); err != nil {
|
|
return err
|
|
}
|
|
pending, err := session.Invoke(ctx, *command, json.RawMessage(`{"text":"Hello, é猫😀é"}`))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
select {
|
|
case result := <-pending:
|
|
if result.Err != nil {
|
|
return result.Err
|
|
}
|
|
fmt.Println(string(result.Value))
|
|
return nil
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|