/ migrations / versions / 040_agentic_browser.py
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}")