040_agentic_browser.py
1 """Agentic browser — project_secrets vault. 2 3 New `project_secrets` table: per-project encrypted credential store that 4 the `browser_fill` builtin tool resolves server-side (plaintext never 5 enters the LLM's context). Admin CRUD via `/projects/{id}/secrets`. 6 7 Companion changes live on `ProjectOptions` (`browser_allowed_domains`, 8 `browser_allow_eval`) — those are JSON-blob fields with no migration. 9 """ 10 import sqlalchemy as sa 11 from alembic import op 12 13 14 revision = '040' 15 down_revision = '039' 16 branch_labels = None 17 depends_on = None 18 19 20 def upgrade(): 21 try: 22 op.create_table( 23 'project_secrets', 24 sa.Column('id', sa.Integer(), primary_key=True, index=True), 25 sa.Column('project_id', sa.Integer(), sa.ForeignKey('projects.id', ondelete='CASCADE'), 26 nullable=False, index=True), 27 sa.Column('name', sa.String(128), nullable=False), 28 sa.Column('value', sa.Text(), nullable=False), # encrypted at rest 29 sa.Column('description', sa.Text(), nullable=True), 30 sa.Column('created_at', sa.DateTime(), nullable=True), 31 sa.Column('updated_at', sa.DateTime(), nullable=True), 32 sa.UniqueConstraint('project_id', 'name', name='uq_project_secret_name'), 33 ) 34 except Exception as e: 35 print(f"Error creating project_secrets table: {e}") 36 37 38 def downgrade(): 39 try: 40 op.drop_table('project_secrets') 41 except Exception as e: 42 print(f"Error dropping project_secrets table: {e}")