feat: add shell execution database migrations (migrations #24-28)

Add database schema for shell command execution, kubeconfig management,
and approval tracking.

- Migration 024: shell_commands table with tier classification
- Migration 025: kubeconfig_files table for encrypted kubeconfig storage
- Migration 026: command_executions table for execution audit trail
- Migration 027: approval_decisions table for session-based approval tracking
- Migration 028: supports_tool_calling column for AI provider capabilities

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Shaun Arman 2026-06-05 08:00:12 -05:00
parent ea170ab340
commit ad2d1ced84

View File

@ -287,6 +287,79 @@ pub fn run_migrations(conn: &Connection) -> anyhow::Result<()> {
"023_add_mcp_env_config",
"ALTER TABLE mcp_servers ADD COLUMN env_config TEXT",
),
(
"024_create_shell_commands",
"CREATE TABLE IF NOT EXISTS shell_commands (
id TEXT PRIMARY KEY,
command_template TEXT NOT NULL,
tier INTEGER NOT NULL CHECK(tier IN (1, 2, 3)),
description TEXT,
category TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT INTO shell_commands (id, command_template, tier, description, category) VALUES
('kubectl_get', 'kubectl get', 1, 'Read Kubernetes resources', 'kubectl'),
('kubectl_describe', 'kubectl describe', 1, 'Describe Kubernetes resources', 'kubectl'),
('kubectl_logs', 'kubectl logs', 1, 'View pod logs', 'kubectl'),
('kubectl_apply', 'kubectl apply', 2, 'Apply configuration', 'kubectl'),
('kubectl_delete', 'kubectl delete', 2, 'Delete resources', 'kubectl'),
('pvecm_status', 'pvecm status', 1, 'Check Proxmox cluster status', 'proxmox'),
('qm_status', 'qm status', 1, 'Check VM status', 'proxmox');",
),
(
"025_create_kubeconfig_files",
"CREATE TABLE IF NOT EXISTS kubeconfig_files (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
encrypted_content TEXT NOT NULL,
context TEXT NOT NULL,
cluster_url TEXT,
is_active INTEGER NOT NULL DEFAULT 0,
uploaded_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_kubeconfig_active ON kubeconfig_files(is_active);",
),
(
"026_create_command_executions",
"CREATE TABLE IF NOT EXISTS command_executions (
id TEXT PRIMARY KEY,
issue_id TEXT,
command TEXT NOT NULL,
tier INTEGER NOT NULL,
approval_status TEXT NOT NULL,
kubeconfig_id TEXT,
exit_code INTEGER,
stdout TEXT,
stderr TEXT,
execution_time_ms INTEGER,
executed_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE,
FOREIGN KEY (kubeconfig_id) REFERENCES kubeconfig_files(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_command_executions_issue ON command_executions(issue_id);
CREATE INDEX IF NOT EXISTS idx_command_executions_executed ON command_executions(executed_at);",
),
(
"027_create_approval_decisions",
"CREATE TABLE IF NOT EXISTS approval_decisions (
id TEXT PRIMARY KEY,
command_pattern TEXT NOT NULL,
decision TEXT NOT NULL CHECK(decision IN ('allow_once', 'allow_session', 'deny')),
session_id TEXT,
decided_at TEXT NOT NULL DEFAULT (datetime('now')),
expires_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_approval_decisions_session ON approval_decisions(session_id);",
),
(
"028_add_supports_tool_calling",
"ALTER TABLE ai_providers ADD COLUMN supports_tool_calling INTEGER DEFAULT 1;
-- Default to true for existing providers to maintain backward compatibility",
),
];
for (name, sql) in migrations {
@ -306,6 +379,8 @@ pub fn run_migrations(conn: &Connection) -> anyhow::Result<()> {
|| name.ends_with("_add_created_at")
|| name.ends_with("_add_log_content_compressed")
|| name.ends_with("_add_image_data")
|| name.ends_with("_add_supports_tool_calling")
|| name.ends_with("_add_mcp_env_config")
{
// Use execute for ALTER TABLE (SQLite only allows one statement per command)
// Skip error if column already exists (SQLITE_ERROR with "duplicate column name")