mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-07 21:13:27 -05:00
Chats and agents can check a PiKVM (kvmd) device and, when a person confirms each call, press the power buttons of the machine it controls and use its keyboard. Read tools: get-info (model, versions, no serial or host name), get-power-state (ATX LEDs) and get-keyboard-state. Write tools: power-action (on, off, reset; off_hard, reset_hard and long_press only with a per-device switch), type-text (bounded, no control characters, refuses key, token and password-list shapes) and send-key (a short allowlist, no Windows or Meta chords). All three declare approval always. The preview reads the power state; apply reads it again and rejects a stale operation. The add-on journals before it sends, never retries and reports unknown when the connection drops. One connection is one device: the address is the single resource of the connection and the one sealed credential holds user:password, split at the first colon. A one-time code is not supported. The add-on speaks only to that address, refuses redirects, uses no proxy and accepts a self-signed certificate only through a #sha256= pin in the address. Typing, the text length and hard power actions are per-device switches that an administrator sets on the add-on page; all start off. Snapshots, mass storage, GPIO and the mouse are left out. The add-on ships a skill, a connection page with a Test button and approval cards, the package files, the library entry and the docs, and joins the package workflow and the case inventory. PiKVM API reference: https://docs.pikvm.org/api/ Assisted-by: Claude:claude-sonnet-5-5 go node
67 lines
2.3 KiB
Go
67 lines
2.3 KiB
Go
// SPDX-License-Identifier: MIT
|
|
|
|
// Package platform is the connector route of the PiKVM add-on. A
|
|
// binding of kind pikvm holds the credential (user:password) and the address
|
|
// of one device as its resource list; the tools read both from the host. The
|
|
// connector carries no messages. The host's Test call asks "me" to check the
|
|
// credential, and this route can only check its shape: the connector request
|
|
// carries the credential and not the address, so the page checks the device
|
|
// itself, with get-info, for each listed address.
|
|
package platform
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/mudler/LocalAI/addons/internal/connector"
|
|
"github.com/mudler/LocalAI/addons/pikvm/internal/kvmd"
|
|
"github.com/mudler/LocalAI/pkg/addons/contract"
|
|
)
|
|
|
|
// Kind is the one kind of binding this add-on serves.
|
|
const Kind = "pikvm"
|
|
|
|
// ErrNoMessages is what the ops that send answer.
|
|
var ErrNoMessages = errors.New("this add-on does not carry messages")
|
|
|
|
// Platform is PiKVM as a connector.Platform.
|
|
type Platform struct{}
|
|
|
|
var _ connector.Platform = Platform{}
|
|
|
|
func (Platform) Kind() string { return Kind }
|
|
func (Platform) MaxRunes() int { return 4000 }
|
|
|
|
// Poll returns no update. It waits for the time the host allows so the loop
|
|
// of an enabled binding does not spin.
|
|
func (Platform) Poll(ctx context.Context, _ string, _ int64, wait int) ([]contract.ConnectorMessage, error) {
|
|
if wait <= 0 {
|
|
return nil, nil
|
|
}
|
|
t := time.NewTimer(time.Duration(wait) * time.Second)
|
|
defer t.Stop()
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case <-t.C:
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func (Platform) Send(context.Context, contract.ConnectorRequest) (string, error) {
|
|
return "", ErrNoMessages
|
|
}
|
|
func (Platform) Edit(context.Context, contract.ConnectorRequest) error { return ErrNoMessages }
|
|
func (Platform) Typing(context.Context, contract.ConnectorRequest) error { return nil }
|
|
|
|
// Me accepts a credential written user:password. It cannot reach the device,
|
|
// because the request has no address, so it never says a login works; the page
|
|
// does that, for each address.
|
|
func (Platform) Me(_ context.Context, token string) (connector.Bot, error) {
|
|
if _, _, err := kvmd.SplitCredential(token); err != nil {
|
|
return connector.Bot{}, connector.ErrUnauthorized
|
|
}
|
|
return connector.Bot{ID: "pikvm", Username: "device user"}, nil
|
|
}
|