Updated documentation, will have to change again later.
This commit is contained in:
1 parent
785569abdd
commit
ba5876f57c
2 files changed
+352
-6
No files matched your search
@@ -7,11 +7,21 @@ Repository material and Hello World are licensed under GPL-3.0-or-later; see
|
|||||||
|
|
||||||
`repository.toml` identifies this source. `catalog.toml` explicitly lists its
|
`repository.toml` identifies this source. `catalog.toml` explicitly lists its
|
||||||
Tentacles, each in a direct top-level folder with its own `tentacle.toml`.
|
Tentacles, each in a direct top-level folder with its own `tentacle.toml`.
|
||||||
The catalog lists verified native packages for Linux amd64 and arm64. Prepared
|
The catalog lists native packages for Linux amd64 and arm64. Hello World 0.1.0 is
|
||||||
release artifacts live in `dist/`; they become downloadable only after publication.
|
available from the [release page](https://git.stormux.org/storm/cthulhu-tentacles/releases/tag/hello-world-v0.1.0).
|
||||||
|
Local build artifacts live in the ignored `dist/` directory.
|
||||||
The discovery source URL is
|
The discovery source URL is
|
||||||
`https://git.stormux.org/storm/cthulhu-tentacles`.
|
`https://git.stormux.org/storm/cthulhu-tentacles`.
|
||||||
|
|
||||||
|
Tentacles are independent of the Git hosting service. Discovery reads TOML files
|
||||||
|
from an HTTPS Git repository; installation downloads the HTTPS URLs explicitly
|
||||||
|
listed in its catalog. Packages can live in pretty much any git provider,
|
||||||
|
on an HTTPS web server, or in object storage with stable public HTTPS
|
||||||
|
links. The reader does not call a hosting service's release API. Keeping packages
|
||||||
|
in separate downloads avoids putting binaries into Git history.
|
||||||
|
|
||||||
|
To write your own, follow the [creation and release tutorial](#create-and-release-your-own-tentacle).
|
||||||
|
|
||||||
## Build and check
|
## Build and check
|
||||||
|
|
||||||
Keep the matching experimental `cthulhu-go` checkout beside this directory.
|
Keep the matching experimental `cthulhu-go` checkout beside this directory.
|
||||||
@@ -86,6 +96,342 @@ changed content; bump the version before preparing a later release.
|
|||||||
The package command creates local artifacts only. It does not create commits,
|
The package command creates local artifacts only. It does not create commits,
|
||||||
releases, tags, uploads or pushes. Prepared catalog changes are not evidence that
|
releases, tags, uploads or pushes. Prepared catalog changes are not evidence that
|
||||||
the corresponding release is already available. Live shortcut, speech, Braille,
|
the corresponding release is already available. Live shortcut, speech, Braille,
|
||||||
lock/handoff and interruption checks remain necessary after publication and an
|
lock/handoff and interruption checks remain necessary after installation. An
|
||||||
explicit reader restart. For source-only local testing, the development launch
|
explicit restart is needed when changing the reader itself. For source-only local
|
||||||
procedure remains in [Hello World](hello-world/README.md#local-launch).
|
testing, the development launch procedure remains in
|
||||||
|
[Hello World](hello-world/README.md#local-launch).
|
||||||
|
|
||||||
|
## Create and release your own Tentacle
|
||||||
|
|
||||||
|
This tutorial creates a native Go Tentacle named **Greeting**, then makes it
|
||||||
|
installable from a repository. The SDK and package format are experimental;
|
||||||
|
use a matching Cthulhu-Go checkout. Currently the installer supports native Linux
|
||||||
|
executables, protocol 1.0, command registration and optional presentation access.
|
||||||
|
Python SDK development is separate from this native package workflow. Broader
|
||||||
|
APIs and automatic package updates are still pending.
|
||||||
|
|
||||||
|
### 1. Set up the source repository
|
||||||
|
|
||||||
|
You need Go 1.24 or newer, Git, Python 3.11 or newer for this repository's checks,
|
||||||
|
GNU tar and gzip. Keep your new repository beside `cthulhu-go`. From their parent
|
||||||
|
directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p greeting-tentacles/greeting/src
|
||||||
|
cd greeting-tentacles
|
||||||
|
git init -b main
|
||||||
|
```
|
||||||
|
|
||||||
|
Create these files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
greeting-tentacles/
|
||||||
|
repository.toml
|
||||||
|
catalog.toml
|
||||||
|
go.mod
|
||||||
|
.gitignore
|
||||||
|
greeting/
|
||||||
|
tentacle.toml
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
src/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this `go.mod`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
module example.org/greeting-tentacles
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
require git.stormux.org/storm/cthulhu-go v0.0.0
|
||||||
|
|
||||||
|
replace git.stormux.org/storm/cthulhu-go => ../cthulhu-go
|
||||||
|
```
|
||||||
|
|
||||||
|
The SDK import path names its source project; it does not constrain where you
|
||||||
|
host your own repository or downloads. Record the tested reader/SDK revision in
|
||||||
|
release notes, since this local replacement does not pin it. Add `/dist/` and
|
||||||
|
`/greeting/bin/` to `.gitignore`.
|
||||||
|
|
||||||
|
Give `greeting/README.md` a description, shortcut, requirements and test procedure.
|
||||||
|
Put the full license text in `greeting/LICENSE`; choose terms you have permission
|
||||||
|
to distribute. For an example derived from Hello World, preserve its notices and
|
||||||
|
GPL-3.0-or-later license. Include source corresponding to your executable.
|
||||||
|
|
||||||
|
Create `repository.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
format-version = 1
|
||||||
|
id = "example.greeting-tentacles"
|
||||||
|
name = "Greeting Tentacles"
|
||||||
|
description = "A small example Tentacle repository."
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the initial `catalog.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
format-version = 1
|
||||||
|
|
||||||
|
[[tentacles]]
|
||||||
|
id = "greeting"
|
||||||
|
manifest = "greeting/tentacle.toml"
|
||||||
|
```
|
||||||
|
|
||||||
|
Manifests belong in direct top-level folders. Repository and Tentacle IDs start
|
||||||
|
with a lowercase letter and contain only lowercase letters, digits, dots and
|
||||||
|
hyphens, up to 128 characters. Keep IDs stable: the reader uses the source URL,
|
||||||
|
repository ID and Tentacle ID to identify an installation. A catalog can list
|
||||||
|
several Tentacles, each with its own manifest and releases. Entries without
|
||||||
|
releases can be discovered, but cannot yet be installed.
|
||||||
|
|
||||||
|
### 2. Describe the package and permissions
|
||||||
|
|
||||||
|
Create `greeting/tentacle.toml`, replacing the example source URL with your own
|
||||||
|
HTTPS Git URL before publication:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
format-version = 1
|
||||||
|
id = "greeting"
|
||||||
|
name = "Greeting"
|
||||||
|
description = "Speak and braille a short greeting."
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Contributor"]
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
source = "https://example.org/projects/greeting-tentacles.git"
|
||||||
|
help = "README.md"
|
||||||
|
runtime = "native"
|
||||||
|
platforms = ["linux"]
|
||||||
|
entry-point = ["bin/greeting"]
|
||||||
|
apis = ["commands", "presentation"]
|
||||||
|
capabilities = ["presentation"]
|
||||||
|
|
||||||
|
[protocol]
|
||||||
|
major = 1
|
||||||
|
minor = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Paths refer to the archive's root, not the source repository's root. The entry
|
||||||
|
point is an executable followed by optional literal arguments, not a shell
|
||||||
|
command. Presentation access allows speech and Braille results through the
|
||||||
|
reader. A command-only package uses `apis = ["commands"]` and
|
||||||
|
`capabilities = []`, without requesting presentation in command registration.
|
||||||
|
Other package API or capability combinations are currently unsupported.
|
||||||
|
|
||||||
|
### 3. Implement a command
|
||||||
|
|
||||||
|
Create `greeting/src/main.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.stormux.org/storm/cthulhu-go/sdk/go/tentacle"
|
||||||
|
)
|
||||||
|
|
||||||
|
func say_greeting(ctx context.Context, _ json.RawMessage) (any, error) {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tentacle.Presentation{
|
||||||
|
Speech: "Hello from Greeting!",
|
||||||
|
Braille: "Hello from Greeting!",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
command := tentacle.Command{
|
||||||
|
ID: "greet", Title: "Greeting",
|
||||||
|
Description: "Speak and braille a greeting", Presentation: true,
|
||||||
|
Bindings: []tentacle.Binding{
|
||||||
|
{Layout: "desktop", Scope: "global", Key: "Cthulhu+Ctrl+Alt+F10"},
|
||||||
|
{Layout: "laptop", Scope: "global", Key: "Cthulhu+Ctrl+Alt+F10"},
|
||||||
|
},
|
||||||
|
Handle: say_greeting,
|
||||||
|
}
|
||||||
|
if tentacle.Serve(os.Stdin, os.Stdout, []tentacle.Command{command}) != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The SDK handles the JSON-RPC session and registration. Stdout belongs exclusively
|
||||||
|
to that protocol; send limited diagnostics to stderr. Respect the supplied
|
||||||
|
context when doing work, including network requests. The reader owns input,
|
||||||
|
speech, Braille, user settings and lifecycle safeguards. Use its public SDK
|
||||||
|
instead of reader internals or output backends. Core and user bindings take
|
||||||
|
precedence over suggested shortcuts; users can remap Tentacle commands.
|
||||||
|
|
||||||
|
Greeting uses ordinary interruptible presentation. Hello World demonstrates
|
||||||
|
`Uninterruptible: true`; use that deliberately, rather than for every message.
|
||||||
|
See its [output behavior](hello-world/README.md) for cancellation, the 30-second
|
||||||
|
protection bound and speech backend limitations.
|
||||||
|
|
||||||
|
### 4. Build and test before packaging
|
||||||
|
|
||||||
|
From `greeting-tentacles`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gofmt -w greeting/src/main.go
|
||||||
|
mkdir -p greeting/bin
|
||||||
|
CGO_ENABLED=0 GOCACHE=/tmp/cthulhu-go-cache go build -buildvcs=false -mod=readonly -trimpath -o greeting/bin/greeting ./greeting/src
|
||||||
|
GOCACHE=/tmp/cthulhu-go-cache go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
Invoke the compiled command through the development host. From `cthulhu-go`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
GOCACHE=/tmp/cthulhu-go-cache go run ./cmd/tentacle-host -presentation -command greet "$PWD/../greeting-tentacles/greeting/bin/greeting"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expect JSON containing `speech` and `braille`, both `Hello from Greeting!`.
|
||||||
|
This runs the executable through the actual command host, but produces no speech
|
||||||
|
or Braille. The harness's default mode has no resource containment; use it only
|
||||||
|
with development code you trust. Protocol tests do not establish live behavior.
|
||||||
|
|
||||||
|
For a live test, follow [Hello World's development launch procedure](hello-world/README.md#local-launch),
|
||||||
|
substituting your absolute executable path and launch ID `greeting`, with
|
||||||
|
`Presentation: true` in the launch JSON. `--replace --tentacles` replaces the
|
||||||
|
current Go reader and selects the development list instead of the saved registry;
|
||||||
|
run it explicitly when ready. Rebuild the reader first if its implementation
|
||||||
|
changed. Production activation requires delegated cgroup resources and does not
|
||||||
|
silently fall back when delegation fails.
|
||||||
|
|
||||||
|
Test the shortcut, learn mode, remapping, ordinary reading and web navigation on
|
||||||
|
Xorg. Verify speech modes, interruption, screen lock, terminal handoff, disable
|
||||||
|
and connected Braille where affected. See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
||||||
|
review and regression requirements; contributors remain responsible for reviewing
|
||||||
|
and testing AI-assisted changes.
|
||||||
|
|
||||||
|
### 5. Create architecture-specific archives
|
||||||
|
|
||||||
|
The `make build`, `make check` and `tools/package.py` commands in **this**
|
||||||
|
repository currently target Hello World specifically. They do not automatically
|
||||||
|
discover or package a new folder. Its checker also pins the current example
|
||||||
|
version. Adapt those tools and checks when extending this repository, or use
|
||||||
|
this standalone procedure for Greeting.
|
||||||
|
|
||||||
|
From `greeting-tentacles`, stage the root manifest, documentation and license:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p dist/greeting-package/bin
|
||||||
|
cp greeting/tentacle.toml greeting/README.md greeting/LICENSE dist/greeting-package/
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the following in Bash for version `0.1.0`. Change the version together with
|
||||||
|
the manifest on later releases. `set -euo pipefail` stops on build/archive failures.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set -euo pipefail
|
||||||
|
version=0.1.0
|
||||||
|
for architecture in amd64 arm64; do
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH="$architecture" GOCACHE=/tmp/cthulhu-go-cache \
|
||||||
|
go build -buildvcs=false -mod=readonly -trimpath -ldflags='-s -w' \
|
||||||
|
-o dist/greeting-package/bin/greeting ./greeting/src
|
||||||
|
tar --sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \
|
||||||
|
-C dist/greeting-package -cf - tentacle.toml README.md LICENSE bin \
|
||||||
|
| gzip -n > "dist/greeting-$version-linux-$architecture.tar.gz"
|
||||||
|
done
|
||||||
|
sha256sum "dist/greeting-$version-linux-amd64.tar.gz" \
|
||||||
|
"dist/greeting-$version-linux-arm64.tar.gz" \
|
||||||
|
| sed 's@ dist/@ @' > dist/SHA256SUMS
|
||||||
|
```
|
||||||
|
|
||||||
|
Each archive must have this layout, without an enclosing `greeting/` folder:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tentacle.toml
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
bin/greeting
|
||||||
|
```
|
||||||
|
|
||||||
|
The executable must be a regular, executable Linux ELF file for the advertised
|
||||||
|
architecture. Static Go builds avoid undeclared shared-library dependencies.
|
||||||
|
The reader rejects unsafe paths, symbolic/hard links and special files. Current
|
||||||
|
limits are 32 MiB per compressed download, 64 MiB expanded file data and 1,024
|
||||||
|
archive entries. Installation runs no install scripts or source builds. Package
|
||||||
|
ID, name, version and capabilities must match the repository manifest.
|
||||||
|
|
||||||
|
Inspect each archive with `tar -tzf`; from `dist`, run `sha256sum -c SHA256SUMS`.
|
||||||
|
Run each extracted executable through the host on its matching architecture;
|
||||||
|
a cross-build alone is not an execution test. The staging executable above ends
|
||||||
|
with the last architecture built, so use the appropriate extracted archive for
|
||||||
|
your local test. Keep the exact archives for publication. Normalized tar/gzip
|
||||||
|
metadata supports repeatable packaging; compare hashes when rebuilding with the
|
||||||
|
same source and toolchain.
|
||||||
|
|
||||||
|
### 6. Publish downloads, then advertise them
|
||||||
|
|
||||||
|
Review the source diff and tests before committing. Push reviewed source to your
|
||||||
|
chosen Git host. For later versions, use a release branch while preparing assets
|
||||||
|
so the default branch's existing manifest and catalog remain consistent. Create
|
||||||
|
a release/tag such as `greeting-v0.1.0` targeting that source commit. If using a
|
||||||
|
separate download server, record that source revision alongside the files.
|
||||||
|
Upload both `.tar.gz` files and `SHA256SUMS`, and make the release public when the
|
||||||
|
assets are ready. Keep `dist/` and executables out of Git.
|
||||||
|
|
||||||
|
Use stable direct HTTPS download URLs. Release attachments are convenient but
|
||||||
|
optional; an ordinary HTTPS file server works too. URLs cannot embed credentials
|
||||||
|
or fragments, and redirects must remain HTTPS. The reader has no authenticated
|
||||||
|
private-download workflow; expiring signed URLs are unsuitable for a durable
|
||||||
|
catalog.
|
||||||
|
|
||||||
|
Download the published files again and check `sha256sum -c SHA256SUMS` against
|
||||||
|
those downloads. Check the archive manifest and executable. Only then append
|
||||||
|
these tables after Greeting's entry in `catalog.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[tentacles.releases]]
|
||||||
|
platform = "linux"
|
||||||
|
architecture = "amd64"
|
||||||
|
url = "https://downloads.example.org/greeting/0.1.0/greeting-0.1.0-linux-amd64.tar.gz"
|
||||||
|
sha256 = "REPLACE_WITH_THE_AMD64_ARCHIVE_SHA256"
|
||||||
|
|
||||||
|
[[tentacles.releases]]
|
||||||
|
platform = "linux"
|
||||||
|
architecture = "arm64"
|
||||||
|
url = "https://downloads.example.org/greeting/0.1.0/greeting-0.1.0-linux-arm64.tar.gz"
|
||||||
|
sha256 = "REPLACE_WITH_THE_ARM64_ARCHIVE_SHA256"
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace the URLs and placeholders with published URLs and their actual
|
||||||
|
64-character lowercase hexadecimal hashes. These tables belong to the preceding
|
||||||
|
`[[tentacles]]`; each platform/architecture pair may appear once. Keep the
|
||||||
|
manifest version equal to the version inside the packages. Include only
|
||||||
|
architectures you actually publish. For several Tentacles, preserve the existing
|
||||||
|
entries: this repository's generated `dist/catalog.toml` lists only Hello World
|
||||||
|
and must not overwrite a larger catalog.
|
||||||
|
|
||||||
|
Review and push the matching catalog and manifest together to the repository's
|
||||||
|
default branch, which discovery fetches. Never replace a release file with changed
|
||||||
|
content: increase the version, create new archives and URLs, then publish a new
|
||||||
|
catalog revision. Refresh updates available metadata, not installed packages.
|
||||||
|
To test a newer version with the current reader, remove that managed Tentacle
|
||||||
|
and install it again after refreshing the source.
|
||||||
|
|
||||||
|
### 7. Verify the published installation
|
||||||
|
|
||||||
|
Open global Preferences → Tentacles → Add repository in the matching reader and
|
||||||
|
enter your HTTPS Git URL, or refresh an existing source. Select Greeting and
|
||||||
|
press Space. Check source, version and presentation access in the first-use
|
||||||
|
dialog, then choose **Install and enable**. Confirm status becomes Active and
|
||||||
|
test Cthulhu+Ctrl+Alt+F10 and the live checks from step 4.
|
||||||
|
|
||||||
|
Also check **Install** without enabling, enable/disable, offline reader restart
|
||||||
|
and removal. Disabled packages retain their files; installed packages launch
|
||||||
|
offline. Removing a source retains installed packages; removing a managed
|
||||||
|
Tentacle stops it and deletes its package files. Keep ordinary reader commands,
|
||||||
|
keyboard navigation and speech/Braille preferences working.
|
||||||
|
|
||||||
|
For no compatible release, check architecture and refresh the source. For
|
||||||
|
download/checksum errors, verify the exact public URL and digest. For package
|
||||||
|
metadata errors, compare the archive manifest with the repository manifest. For
|
||||||
|
activation failures, check the reader log for protocol, executable or cgroup
|
||||||
|
errors. Resource limits constrain consumption; they do not sandbox access to
|
||||||
|
account files or network. Install publishers you trust and state any remaining
|
||||||
|
unverified live behavior in release notes.
|
||||||
+1
-1
@@ -94,7 +94,7 @@ def check_host(repoRoot, programPath):
|
|||||||
environment = os.environ.copy()
|
environment = os.environ.copy()
|
||||||
environment.setdefault("GOCACHE", "/tmp/cthulhu-go-cache")
|
environment.setdefault("GOCACHE", "/tmp/cthulhu-go-cache")
|
||||||
result = subprocess.run([
|
result = subprocess.run([
|
||||||
"go", "run", "./examples/tentacles/host", "-presentation",
|
"go", "run", "./cmd/tentacle-host", "-presentation",
|
||||||
"-command", "hello-world", str(programPath),
|
"-command", "hello-world", str(programPath),
|
||||||
], cwd=readerRoot, env=environment, capture_output=True, text=True, timeout=60, check=True)
|
], cwd=readerRoot, env=environment, capture_output=True, text=True, timeout=60, check=True)
|
||||||
require(json.loads(result.stdout) == {
|
require(json.loads(result.stdout) == {
|
||||||
|
|||||||
Reference in new issue
Block a user