202 lines
8.1 KiB
Go
202 lines
8.1 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
|
|
"forge-tools-lexicomai/internal/lexicomai"
|
|
|
|
"git.totmin.ru/en2zmax/forge-toolkit"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// registerInstall — мутации (M3–M5): установка, обновление, удаление, база,
|
|
// конфигурация, лицензия, управление службой. Все через addAction (forge-probe)
|
|
// → хост применяет политику approval; домен дополнительно проверяет
|
|
// read_only/allow_mutate.
|
|
func registerInstall(r *registrar) {
|
|
r.addAction("service_install",
|
|
"Установка продукта на машину (асинхронно, возвращает id). Плюс последняя версия в ветке.",
|
|
schema(map[string]any{
|
|
"product": strProps("Продукт компании", true, lexicomai.DefaultProducts()...),
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
"version": strProps("Версия (например 7.012); пусто = последняя", false),
|
|
"branch": strProps("release или beta (default release)", false, "release", "beta"),
|
|
}, []string{"product"}),
|
|
patternsFor("install"),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
p, err := productArg(args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
id, err := r.m.Install(ctx, getString(args, "alias", ""), p,
|
|
getString(args, "version", ""), getString(args, "branch", "release"))
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult("install started, id=" + id), nil
|
|
})
|
|
|
|
r.add("service_install_status",
|
|
"Прогресс установки по id (service_install). Read-only.",
|
|
schema(map[string]any{
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
"id": strProps("id установки", true),
|
|
}, []string{"id"}),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
id, err := requireString(args, "id")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
sess, ok := r.m.Session(id)
|
|
if !ok {
|
|
return errorResult("install session " + id + " not found (id is per-process)"), nil
|
|
}
|
|
t, err := targetArg(r.m, args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
out, err := r.m.InstallStatus(ctx, t, sess)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(out), nil
|
|
})
|
|
|
|
r.addAction("service_uninstall",
|
|
"Удаление продукта с машины (инсталлятор --uninstall).",
|
|
schema(map[string]any{
|
|
"product": strProps("Продукт компании", true, lexicomai.DefaultProducts()...),
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
}, []string{"product"}),
|
|
patternsFor("uninstall"),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
p, err := productArg(args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
out, err := r.m.Uninstall(ctx, getString(args, "alias", ""), p)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(out), nil
|
|
})
|
|
|
|
r.addAction("service_update",
|
|
"Обновление продукта до последней версии в ветке (backup инсталлятором).",
|
|
schema(map[string]any{
|
|
"product": strProps("Продукт компании", true, lexicomai.DefaultProducts()...),
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
"branch": strProps("release или beta (default release)", false, "release", "beta"),
|
|
}, []string{"product"}),
|
|
patternsFor("update"),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
p, err := productArg(args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
id, err := r.m.Install(ctx, getString(args, "alias", ""), p, "", getString(args, "branch", "release"))
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult("update started, id=" + id), nil
|
|
})
|
|
|
|
r.addAction("base_ensure",
|
|
"Создание минимальной базы /opt/pve на машине (идемпотентно).",
|
|
schema(map[string]any{
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
}, nil),
|
|
func(map[string]any) ([]string, []string) { return []string{"lexicomai base"}, nil },
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
out, err := r.m.BaseEnsure(ctx, getString(args, "alias", ""))
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(out), nil
|
|
})
|
|
|
|
r.addAction("service_config_set",
|
|
"Изменение ключей config.json (точечный путь, например logs.level) + рестарт.",
|
|
schema(map[string]any{
|
|
"product": strProps("Продукт компании", true, lexicomai.DefaultProducts()...),
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
"key": strProps("Ключ (точечный путь, например logs.level)", true),
|
|
"value": strProps("Новое значение (строка)", true),
|
|
}, []string{"product", "key", "value"}),
|
|
patternsFor("config"),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
p, err := productArg(args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
key, err := requireString(args, "key")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
out, err := r.m.ConfigSet(ctx, getString(args, "alias", ""), p, map[string]any{key: getString(args, "value", "")})
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(out), nil
|
|
})
|
|
|
|
r.addAction("license_apply",
|
|
"Запись license.json и проверка через /license/check.",
|
|
schema(map[string]any{
|
|
"product": strProps("Продукт компании", true, lexicomai.DefaultProducts()...),
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
"license": strProps("JSON лицензии (service/expire/licenses/key)", true),
|
|
}, []string{"product", "license"}),
|
|
patternsFor("license"),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
p, err := productArg(args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
lic, err := requireString(args, "license")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
out, err := r.m.LicenseApply(ctx, getString(args, "alias", ""), p, lic)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(out), nil
|
|
})
|
|
|
|
r.addAction("service_restart",
|
|
"Перезапуск службы продукта (systemctl restart).",
|
|
schema(map[string]any{
|
|
"product": strProps("Продукт компании", true, lexicomai.DefaultProducts()...),
|
|
"alias": strProps("Машина (target alias); пусто = по умолчанию", false),
|
|
}, []string{"product"}),
|
|
patternsFor("restart"),
|
|
func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := requestArgs(req)
|
|
p, err := productArg(args)
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
out, err := r.m.ServiceControl(ctx, getString(args, "alias", ""), p, "restart")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(out), nil
|
|
})
|
|
}
|
|
|
|
// patternsFor строит permission-паттерн «lexicomai <action> <product>».
|
|
func patternsFor(action string) toolkit.PatternsFn {
|
|
return func(args map[string]any) ([]string, []string) {
|
|
p := getString(args, "product", "")
|
|
return []string{"lexicomai " + action + " " + p}, nil
|
|
}
|
|
}
|