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.`, 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 _deleted_ 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."), } }