letta crash-loops on 'type "vector" does not exist': pgvector is not a trusted extension, so only the provider's superuser can create it, and the provisioner never did. A contribution may now name extensions; the provider creates each (IF NOT EXISTS, available ones only) in the consumer's database on every pass. Go per the standing rule for a TypeScript module that changes. letta asks for vector.
99 lines
3.7 KiB
Go
99 lines
3.7 KiB
Go
package main
|
|
|
|
// postgres's tools, and the mesh-store seat's verbs (novox/hq ADR 0159, 0160). The seat's verbs are
|
|
// named `mesh-store.<verb>`, so the runtime serves them as the seat's wherever this module holds it
|
|
// and never lists them as postgres's own; they are scoped to what the store enables — asking what it
|
|
// holds and reading from it — so retiring a database is postgres's tool and not the store's.
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// Seat is the store seat this module claims.
|
|
const Seat = "mesh-store"
|
|
|
|
var queryInput = map[string]any{
|
|
"database": map[string]any{"type": "string", "description": "the database to query"},
|
|
"sql": map[string]any{"type": "string", "description": "the SELECT (or other read-only) statement"},
|
|
}
|
|
|
|
func str(args map[string]any, key string) string {
|
|
if s, ok := args[key].(string); ok {
|
|
return s
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func listTool(pg *Client, name, description string) stdio.Tool {
|
|
return stdio.Tool{Name: name, Description: description, Run: func(map[string]any) (any, error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
dbs, err := pg.ListDatabases(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return map[string]any{"databases": dbs}, nil
|
|
}}
|
|
}
|
|
|
|
func queryTool(pg *Client, name, prefix, description string) stdio.Tool {
|
|
return stdio.Tool{Name: name, Description: description, Input: queryInput, Run: func(args map[string]any) (any, error) {
|
|
database, sql := str(args, "database"), str(args, "sql")
|
|
if database == "" {
|
|
return nil, fmt.Errorf("%s: database is required", prefix)
|
|
}
|
|
if sql == "" {
|
|
return nil, fmt.Errorf("%s: sql is required", prefix)
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
|
defer cancel()
|
|
r, err := pg.ReadOnlyQuery(ctx, database, sql)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return map[string]any{"database": database, "command": r.Command, "rows": r.Maps()}, nil
|
|
}}
|
|
}
|
|
|
|
// Tools are postgres's own and the seat's verbs.
|
|
func Tools(pg *Client) []stdio.Tool {
|
|
return []stdio.Tool{
|
|
listTool(pg, "postgres_list_databases", "List the databases on the postgres server, with their on-disk size."),
|
|
queryTool(pg, "postgres_query", "postgres_query",
|
|
"Run a read-only SQL query against a named database, as a login that can read every table and change nothing."),
|
|
{
|
|
Name: "postgres_retire_database",
|
|
Description: "Take one database out of service on purpose: rename it to <name>_deleted_<date> and lock its owner's login. " +
|
|
"Nothing is dropped — the data stays on the server under the new name until a person removes it by hand. " +
|
|
"Repeat the database's name in confirm.",
|
|
Input: map[string]any{
|
|
"database": map[string]any{"type": "string", "description": "the database to retire"},
|
|
"confirm": map[string]any{"type": "string", "description": "the same name again, to say this is meant"},
|
|
},
|
|
Run: func(args map[string]any) (any, error) {
|
|
database := str(args, "database")
|
|
if database == "" {
|
|
return nil, errors.New("postgres_retire_database: database is required")
|
|
}
|
|
if str(args, "confirm") != database {
|
|
return nil, errors.New("postgres_retire_database: confirm must repeat the database's name")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
aside, err := pg.RetireDatabase(ctx, database, time.Now())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return map[string]any{"database": database, "renamedTo": aside, "dropped": false}, nil
|
|
},
|
|
},
|
|
listTool(pg, Seat+".databases", "Every database the store holds, with its on-disk size."),
|
|
queryTool(pg, Seat+".query", "query", "One read-only statement against one database the store holds."),
|
|
}
|
|
}
|