Cletus plugin kit: rules for coding agents, specification, host API, style guide, worker SDK and cletus-plugin tool
This commit is contained in:
commit
9b19d3a8be
41 files changed
+4121
No files matched your search
@@ -0,0 +1,90 @@
|
||||
package pluginsdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Host makes calls to Cletus on behalf of the request being handled. Use
|
||||
// it only inside the handler it was passed to.
|
||||
type Host struct {
|
||||
call func(ctx context.Context, method string, params any) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
// NewTestHost returns a Host whose calls are answered by fn, for unit tests
|
||||
// of handlers. fn receives the method and the params as JSON.
|
||||
func NewTestHost(fn func(method string, params json.RawMessage) (any, error)) *Host {
|
||||
return &Host{call: func(_ context.Context, method string, params any) (json.RawMessage, error) {
|
||||
raw, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := fn(method, raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(result)
|
||||
}}
|
||||
}
|
||||
|
||||
// Call makes any host call in docs/host-api.md and decodes its result into
|
||||
// out (which may be nil).
|
||||
func (h *Host) Call(ctx context.Context, method string, params, out any) error {
|
||||
if params == nil {
|
||||
params = struct{}{}
|
||||
}
|
||||
raw, err := h.call(ctx, method, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if out == nil || len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := json.Unmarshal(raw, out); err != nil {
|
||||
return fmt.Errorf("%s: unexpected result: %w", method, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolvePath returns the real path of a file the user chose
|
||||
// (host.files.resolve; needs the files permission). Resolve every path
|
||||
// before reading it.
|
||||
func (h *Host) ResolvePath(ctx context.Context, path string) (string, error) {
|
||||
var resolved string
|
||||
err := h.Call(ctx, "host.files.resolve", map[string]string{"path": path}, &resolved)
|
||||
return resolved, err
|
||||
}
|
||||
|
||||
// CanWrite reports whether the user's current rules allow writing path
|
||||
// (host.files.can_write; needs the files permission).
|
||||
func (h *Host) CanWrite(ctx context.Context, path string) (bool, error) {
|
||||
var allowed bool
|
||||
err := h.Call(ctx, "host.files.can_write", map[string]string{"path": path}, &allowed)
|
||||
return allowed, err
|
||||
}
|
||||
|
||||
// Event sends an event to the plugin's screen. name must start with the
|
||||
// plugin id and a colon ("acme.gauge-check:progress").
|
||||
func (h *Host) Event(ctx context.Context, name string, args ...any) error {
|
||||
if args == nil {
|
||||
args = []any{}
|
||||
}
|
||||
return h.Call(ctx, "host.event", map[string]any{"name": name, "args": args}, nil)
|
||||
}
|
||||
|
||||
// Published is a link to a file in the data folder that the screen can fetch.
|
||||
type Published struct {
|
||||
Token string `json:"token"`
|
||||
URL string `json:"url"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
}
|
||||
|
||||
// Publish makes a file in the data folder fetchable by the screen for ttl
|
||||
// (1 second to 1 hour). The file must not change while published.
|
||||
func (h *Host) Publish(ctx context.Context, path, contentType string, ttl time.Duration) (Published, error) {
|
||||
var out Published
|
||||
err := h.Call(ctx, "host.data.publish", map[string]any{"path": path, "contentType": contentType, "ttl": int(ttl / time.Second)}, &out)
|
||||
return out, err
|
||||
}
|
||||
Reference in new issue
Block a user