opsmith-cli 0.2.2b0__tar.gz → 0.3.1b0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/.gitignore +4 -1
  2. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/PKG-INFO +10 -17
  3. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/README.md +9 -16
  4. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/agent.py +6 -1
  5. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/deployment_strategies/base.py +7 -7
  6. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/deployment_strategies/monolithic.py +14 -17
  7. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/git_repo.py +2 -1
  8. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/infra_provisioners/base_provisioner.py +1 -3
  9. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/main.py +44 -38
  10. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/models.py +3 -2
  11. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/prompts.py +2 -0
  12. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/repo_map.py +4 -3
  13. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/service_detector.py +46 -23
  14. opsmith_cli-0.3.1b0/opsmith/templates/dockerfiles/python_backend_api +79 -0
  15. opsmith_cli-0.3.1b0/opsmith/templates/dockerfiles/python_backend_worker +77 -0
  16. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/pyproject.toml +4 -1
  17. opsmith_cli-0.3.1b0/resources/deploy_demo.gif +0 -0
  18. opsmith_cli-0.3.1b0/resources/deploy_demo.svg +1 -0
  19. opsmith_cli-0.3.1b0/resources/setup_demo.gif +0 -0
  20. opsmith_cli-0.3.1b0/resources/setup_demo.svg +1 -0
  21. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/.flake8 +0 -0
  22. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/.github/workflows/python-publish.yml +0 -0
  23. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/.pre-commit-config.yaml +0 -0
  24. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/CONVENTIONS.md +0 -0
  25. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/LICENSE.txt +0 -0
  26. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/__init__.py +0 -0
  27. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/cloud_providers/__init__.py +0 -0
  28. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/cloud_providers/aws.py +0 -0
  29. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/cloud_providers/base.py +0 -0
  30. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/cloud_providers/gcp.py +0 -0
  31. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/constants.py +0 -0
  32. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/deployment_strategies/__init__.py +0 -0
  33. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/exceptions.py +0 -0
  34. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/infra_provisioners/__init__.py +0 -0
  35. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/infra_provisioners/ansible_provisioner.py +0 -0
  36. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/infra_provisioners/terraform_provisioner.py +0 -0
  37. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/README.md +0 -0
  38. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/arduino-tags.scm +0 -0
  39. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/c-tags.scm +0 -0
  40. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
  41. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/chatito-tags.scm +0 -0
  42. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/commonlisp-tags.scm +0 -0
  43. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/cpp-tags.scm +0 -0
  44. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/csharp-tags.scm +0 -0
  45. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/d-tags.scm +0 -0
  46. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/dart-tags.scm +0 -0
  47. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/elisp-tags.scm +0 -0
  48. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/elixir-tags.scm +0 -0
  49. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/elm-tags.scm +0 -0
  50. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/gleam-tags.scm +0 -0
  51. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/go-tags.scm +0 -0
  52. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/hcl-tags.scm +0 -0
  53. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/java-tags.scm +0 -0
  54. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/javascript-tags.scm +0 -0
  55. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
  56. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/lua-tags.scm +0 -0
  57. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
  58. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/ocaml_interface-tags.scm +0 -0
  59. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/php-tags.scm +0 -0
  60. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/pony-tags.scm +0 -0
  61. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/properties-tags.scm +0 -0
  62. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/python-tags.scm +0 -0
  63. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/ql-tags.scm +0 -0
  64. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/r-tags.scm +0 -0
  65. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/racket-tags.scm +0 -0
  66. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/ruby-tags.scm +0 -0
  67. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/rust-tags.scm +0 -0
  68. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/scala-tags.scm +0 -0
  69. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/solidity-tags.scm +0 -0
  70. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/swift-tags.scm +0 -0
  71. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/typescript-tags.scm +0 -0
  72. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/queries/tree-sitter-languages/udev-tags.scm +0 -0
  73. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/settings.py +0 -0
  74. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/cloud_storage_cleanup/aws/main.yml +0 -0
  75. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/cloud_storage_cleanup/gcp/main.yml +0 -0
  76. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/container_registry/aws/main.tf +0 -0
  77. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/container_registry/aws/outputs.tf +0 -0
  78. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/container_registry/aws/variables.tf +0 -0
  79. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/container_registry/gcp/main.tf +0 -0
  80. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/container_registry/gcp/outputs.tf +0 -0
  81. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/container_registry/gcp/variables.tf +0 -0
  82. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_build_push/aws/main.yml +0 -0
  83. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_build_push/gcp/main.yml +0 -0
  84. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_deploy/aws/main.yml +0 -0
  85. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_deploy/gcp/main.yml +0 -0
  86. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_run/common/main.yml +0 -0
  87. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/base.yml +0 -0
  88. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/elasticsearch.yml +0 -0
  89. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/kafka.yml +0 -0
  90. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/mongodb.yml +0 -0
  91. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/mysql.yml +0 -0
  92. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/postgresql.yml +0 -0
  93. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/rabbitmq.yml +0 -0
  94. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/redis.yml +0 -0
  95. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/services/backend_api.yml +0 -0
  96. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/services/backend_worker.yml +0 -0
  97. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/services/full_stack.yml +0 -0
  98. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/traefik.yml +0 -0
  99. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_compose_snippets/weaviate.yml +0 -0
  100. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_setup/aws/main.yml +0 -0
  101. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/docker_setup/gcp/main.yml +0 -0
  102. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/fetch_remote_files/common/main.yml +0 -0
  103. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_bucket_cert/aws/main.tf +0 -0
  104. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_bucket_cert/aws/outputs.tf +0 -0
  105. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_bucket_cert/aws/variables.tf +0 -0
  106. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_bucket_cert/gcp/main.tf +0 -0
  107. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_bucket_cert/gcp/outputs.tf +0 -0
  108. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_bucket_cert/gcp/variables.tf +0 -0
  109. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_cdn/aws/main.tf +0 -0
  110. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_cdn/aws/outputs.tf +0 -0
  111. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_cdn/aws/variables.tf +0 -0
  112. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_cdn/gcp/main.tf +0 -0
  113. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_cdn/gcp/outputs.tf +0 -0
  114. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_cdn/gcp/variables.tf +0 -0
  115. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_deploy/aws/main.yml +0 -0
  116. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/frontend_deploy/gcp/main.yml +0 -0
  117. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/virtual_machine/aws/main.tf +0 -0
  118. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/virtual_machine/aws/outputs.tf +0 -0
  119. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/virtual_machine/aws/variables.tf +0 -0
  120. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/virtual_machine/gcp/main.tf +0 -0
  121. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/virtual_machine/gcp/outputs.tf +0 -0
  122. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/templates/virtual_machine/gcp/variables.tf +0 -0
  123. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/types.py +7 -7
  124. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/opsmith/utils.py +0 -0
  125. {opsmith_cli-0.2.2b0 → opsmith_cli-0.3.1b0}/uv.lock +0 -0
@@ -194,4 +194,7 @@ cython_debug/
194
194
  .cursorindexingignore
195
195
 
196
196
  # Aider
197
- .aider*
197
+ .aider*
198
+
199
+ # Asciinema
200
+ *.cast
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opsmith-cli
3
- Version: 0.2.2b0
3
+ Version: 0.3.1b0
4
4
  Summary: Opsmith is an AI devops engineer in your terminal
5
5
  License-Expression: GPL-3.0-only
6
6
  License-File: LICENSE.txt
@@ -109,13 +109,10 @@ Deploying your application with Opsmith follows a straightforward workflow:
109
109
  ```shell
110
110
  opsmith --model <your-llm-provider:model-name> --api-key <your-api-key> setup
111
111
  ```
112
-
113
- You will be prompted to:
114
- - Provide an application name.
115
- - Select a cloud provider (e.g., AWS, GCP — see [Cloud Providers](#cloud-providers) for setup).
116
- - Review and confirm the services and infrastructure dependencies detected by the AI.
117
- - Opsmith will then generate a `Dockerfile` for each of your services.
118
-
112
+ <p >
113
+ <img width="600" src="https://raw.githubusercontent.com/abhishek-ram/opsmith-cli/main/resources/setup_demo.svg" alt="Opsmith Setup">
114
+ </p>
115
+
119
116
  2. **Deploy Your Application**
120
117
 
121
118
  After setting up the configuration, deploy your application using the `deploy` command:
@@ -124,13 +121,9 @@ Deploying your application with Opsmith follows a straightforward workflow:
124
121
  opsmith --model <your-llm-provider:model-name> --api-key <your-api-key> deploy
125
122
  ```
126
123
 
127
- This will guide you through:
128
- - Creating a new deployment environment (e.g., `dev`, `staging`, `production`).
129
- - Selecting a cloud region.
130
- - Choosing a deployment strategy (e.g., `Monolithic`).
131
- - Configuring domain names for your services if needed.
132
-
133
- Opsmith will then provision all the necessary cloud infrastructure and deploy your application.
124
+ <p >
125
+ <img width="600" src="https://raw.githubusercontent.com/abhishek-ram/opsmith-cli/main/resources/deploy_demo.svg" alt="Opsmith Setup">
126
+ </p>
134
127
 
135
128
  3. **Manage Your Deployments**
136
129
 
@@ -149,14 +142,14 @@ Opsmith leverages Large Language Models (LLMs) to analyze your codebase, generat
149
142
 
150
143
  Opsmith supports a variety of models from different providers. Here is a list of the currently supported models:
151
144
 
145
+ - **Google** (Recommended):
146
+ - `google-gla:gemini-2.5-pro`
152
147
  - **OpenAI**:
153
148
  - `openai:gpt-4.1`
154
149
  - `openai:gpt-o3`
155
150
  - **Anthropic**:
156
151
  - `anthropic:claude-3-7-sonnet-20250219`
157
152
  - `anthropic:claude-sonnet-4-20250514`
158
- - **Google**:
159
- - `google-gla:gemini-2.5-pro`
160
153
 
161
154
  #### Usage
162
155
 
@@ -70,13 +70,10 @@ Deploying your application with Opsmith follows a straightforward workflow:
70
70
  ```shell
71
71
  opsmith --model <your-llm-provider:model-name> --api-key <your-api-key> setup
72
72
  ```
73
-
74
- You will be prompted to:
75
- - Provide an application name.
76
- - Select a cloud provider (e.g., AWS, GCP — see [Cloud Providers](#cloud-providers) for setup).
77
- - Review and confirm the services and infrastructure dependencies detected by the AI.
78
- - Opsmith will then generate a `Dockerfile` for each of your services.
79
-
73
+ <p >
74
+ <img width="600" src="https://raw.githubusercontent.com/abhishek-ram/opsmith-cli/main/resources/setup_demo.svg" alt="Opsmith Setup">
75
+ </p>
76
+
80
77
  2. **Deploy Your Application**
81
78
 
82
79
  After setting up the configuration, deploy your application using the `deploy` command:
@@ -85,13 +82,9 @@ Deploying your application with Opsmith follows a straightforward workflow:
85
82
  opsmith --model <your-llm-provider:model-name> --api-key <your-api-key> deploy
86
83
  ```
87
84
 
88
- This will guide you through:
89
- - Creating a new deployment environment (e.g., `dev`, `staging`, `production`).
90
- - Selecting a cloud region.
91
- - Choosing a deployment strategy (e.g., `Monolithic`).
92
- - Configuring domain names for your services if needed.
93
-
94
- Opsmith will then provision all the necessary cloud infrastructure and deploy your application.
85
+ <p >
86
+ <img width="600" src="https://raw.githubusercontent.com/abhishek-ram/opsmith-cli/main/resources/deploy_demo.svg" alt="Opsmith Setup">
87
+ </p>
95
88
 
96
89
  3. **Manage Your Deployments**
97
90
 
@@ -110,14 +103,14 @@ Opsmith leverages Large Language Models (LLMs) to analyze your codebase, generat
110
103
 
111
104
  Opsmith supports a variety of models from different providers. Here is a list of the currently supported models:
112
105
 
106
+ - **Google** (Recommended):
107
+ - `google-gla:gemini-2.5-pro`
113
108
  - **OpenAI**:
114
109
  - `openai:gpt-4.1`
115
110
  - `openai:gpt-o3`
116
111
  - **Anthropic**:
117
112
  - `anthropic:claude-3-7-sonnet-20250219`
118
113
  - `anthropic:claude-sonnet-4-20250514`
119
- - **Google**:
120
- - `google-gla:gemini-2.5-pro`
121
114
 
122
115
  #### Usage
123
116
 
@@ -1,5 +1,5 @@
1
1
  import json
2
- from dataclasses import dataclass
2
+ from dataclasses import dataclass, field
3
3
  from pathlib import Path
4
4
  from typing import List, Type
5
5
 
@@ -13,6 +13,7 @@ from opsmith.utils import generate_secret_string
13
13
  @dataclass
14
14
  class AgentDeps:
15
15
  src_dir: Path
16
+ tracked_files: List[Path] = field(default_factory=list)
16
17
 
17
18
 
18
19
  def is_duplicate_tool_call(ctx: RunContext[AgentDeps], tool_name: str) -> bool:
@@ -64,6 +65,7 @@ def build_agent(model_config: Type[BaseAiModel], instrument: bool = False) -> Ag
64
65
  "files in this conversation."
65
66
  )
66
67
 
68
+ allowed_files_abs = {str(p) for p in ctx.deps.tracked_files}
67
69
  contents = []
68
70
  for filename in filenames:
69
71
  if Path(filename).is_absolute():
@@ -74,6 +76,9 @@ def build_agent(model_config: Type[BaseAiModel], instrument: bool = False) -> Ag
74
76
 
75
77
  absolute_file_path = ctx.deps.src_dir.joinpath(filename).resolve()
76
78
 
79
+ if str(absolute_file_path) not in allowed_files_abs:
80
+ raise ModelRetry(f"File '{filename}' is not in the repo map and cannot be read.")
81
+
77
82
  if not str(absolute_file_path).startswith(str(ctx.deps.src_dir)):
78
83
  raise ModelRetry(
79
84
  f"Access denied. File '{filename}' is outside the repository root."
@@ -113,14 +113,14 @@ class BaseDeploymentStrategy(abc.ABC):
113
113
  # Registry name should probably be unique per region for the app
114
114
  registry_name = slugify(f"{app_name}-{environment.region}")
115
115
 
116
- cloud_provider_instance = deployment_config.cloud_provider_instance
116
+ cloud_provider_instance = environment.cloud_provider_instance
117
117
  provider_name = cloud_provider_instance.name()
118
118
 
119
119
  registry_infra_path = (
120
120
  self.deployments_path
121
121
  / "environments"
122
122
  / "global"
123
- / environment.region
123
+ / f"{cloud_provider_instance.name()}-{environment.region}"
124
124
  / "container_registry"
125
125
  )
126
126
  tf = TerraformProvisioner(working_dir=registry_infra_path)
@@ -162,7 +162,7 @@ class BaseDeploymentStrategy(abc.ABC):
162
162
  registry_url: str,
163
163
  ) -> Dict[str, str]:
164
164
  """Builds and pushes Docker images for each service."""
165
- print("\n[bold blue]Starting to build and push images...[/bold blue]")
165
+ print("\n[bold blue]Build and push container images to the registry[/bold blue]")
166
166
 
167
167
  images = {}
168
168
 
@@ -201,7 +201,7 @@ class BaseDeploymentStrategy(abc.ABC):
201
201
 
202
202
  ansible_runner = AnsibleProvisioner(working_dir=build_infra_path)
203
203
 
204
- provider_name = deployment_config.cloud_provider["name"].lower()
204
+ provider_name = environment.cloud_provider["name"].lower()
205
205
 
206
206
  extra_vars = {
207
207
  "docker_path": str(self.agent_deps.src_dir),
@@ -237,7 +237,7 @@ class BaseDeploymentStrategy(abc.ABC):
237
237
  :return: The content of the found SSH public key as a string.
238
238
  :rtype: str
239
239
  """
240
- print("\n[bold blue]Checking for local SSH public key...[/bold blue]")
240
+ print("\n[bold blue]Looking up the local SSH public key[/bold blue]")
241
241
  system = platform.system().lower()
242
242
 
243
243
  # Common SSH key names
@@ -342,10 +342,10 @@ class BaseDeploymentStrategy(abc.ABC):
342
342
  print("[bold red]Could not find 'ansible_user' in Terraform outputs.[/bold red]")
343
343
  raise ValueError("Could not find 'ansible_user' in Terraform outputs.")
344
344
 
345
- print("\n[bold blue]Waiting 15 seconds for instance to become ready...[/bold blue]")
345
+ print("[bold blue]Waiting 15 seconds for instance to become ready...[/bold blue]")
346
346
  time.sleep(15)
347
347
 
348
- print("\n[bold blue]Setting up Docker on the new instance...[/bold blue]")
348
+ print("\n[bold blue]Setting up Docker on the newly created VM[/bold blue]")
349
349
 
350
350
  setup_docker_path = (
351
351
  self.deployments_path / "environments" / environment.name / "docker_setup"
@@ -153,17 +153,16 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
153
153
  """
154
154
  Deploys the docker-compose stack and returns container logs for validation.
155
155
  """
156
- print("\n[bold blue]Deploying docker-compose stack...[/bold blue]")
156
+ print("\n[bold blue]Deploying docker-compose stack to the VM \n[/bold blue]")
157
157
  ansible_user, instance_public_ip = (
158
158
  environment_state.virtual_machine.user,
159
159
  environment_state.virtual_machine.public_ip,
160
160
  )
161
161
  deploy_compose_path, docker_compose_path = self._get_deploy_docker_compose_path(environment)
162
162
 
163
- print("\n[bold blue]Attempting to deploy and get logs...[/bold blue]")
164
163
  ansible_runner = AnsibleProvisioner(working_dir=deploy_compose_path)
165
164
  ansible_runner.copy_template(
166
- "docker_compose_deploy", deployment_config.cloud_provider_instance.name().lower()
165
+ "docker_compose_deploy", environment.cloud_provider_instance.name().lower()
167
166
  )
168
167
 
169
168
  traefik_template = self.docker_compose_snippets_env.get_template("traefik.yml")
@@ -241,8 +240,6 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
241
240
  images: Dict[str, str],
242
241
  environment_state: MonolithicDeploymentState,
243
242
  ):
244
- print("\n[bold blue]Generating docker-compose file...[/bold blue]")
245
-
246
243
  base_compose_template = self.docker_compose_snippets_env.get_template("base.yml")
247
244
  base_compose = base_compose_template.render(app_name=deployment_config.app_name_slug)
248
245
 
@@ -297,8 +294,8 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
297
294
  messages = []
298
295
  for attempt in range(settings.max_docker_compose_gen_attempts):
299
296
  print(
300
- "\n[bold]Attempt"
301
- f" {attempt + 1}/{settings.max_docker_compose_gen_attempts}...[/bold]"
297
+ "\n[bold blue]Generating docker-compose file, Attempt"
298
+ f" {attempt + 1}/{settings.max_docker_compose_gen_attempts}[/bold blue]"
302
299
  )
303
300
  prompt = DOCKER_COMPOSE_GENERATION_PROMPT_TEMPLATE.format(
304
301
  base_compose=base_compose,
@@ -338,7 +335,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
338
335
  if is_successful:
339
336
  print("[bold green]Docker compose deployment was successful.[/bold green]")
340
337
  break
341
- print(f"Docker compose validation 'failed' with reason: \n {reason}.")
338
+ print(f"[red]Docker compose validation 'failed' with reason[/red]: \n {reason}.")
342
339
 
343
340
  messages = docker_compose_response.new_messages() + validation_messages
344
341
  else:
@@ -637,7 +634,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
637
634
  s for s in deployment_config.services if s.service_type != ServiceTypeEnum.FRONTEND
638
635
  ]
639
636
 
640
- cloud_provider = deployment_config.cloud_provider_instance
637
+ cloud_provider = environment.cloud_provider_instance
641
638
  env_state_path = self._get_env_state_path(environment.name)
642
639
 
643
640
  env_state = MonolithicDeploymentState()
@@ -699,7 +696,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
699
696
 
700
697
  print(
701
698
  "\n[bold blue]Setting up container registry for region"
702
- f" '{environment.region}'...[/bold blue]"
699
+ f" '{environment.region}'... \n[/bold blue]"
703
700
  )
704
701
  registry_url = self._setup_container_registry(deployment_config, environment)
705
702
  images = self._build_and_push_images(deployment_config, environment, registry_url)
@@ -743,16 +740,16 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
743
740
  )
744
741
  self._confirm_dns_records(dns_records)
745
742
 
743
+ env_state.registry_url = registry_url
744
+ env_state.virtual_machine = virtual_machine_state
745
+ self._generate_docker_compose(deployment_config, environment, images, env_state)
746
+
746
747
  for domain in environment.get_domains_for_services(other_services):
747
748
  print(
748
749
  "\n[bold green]Your website is available at:"
749
750
  f" https://{domain.domain_name}[/bold green]"
750
751
  )
751
752
 
752
- env_state.registry_url = registry_url
753
- env_state.virtual_machine = virtual_machine_state
754
- self._generate_docker_compose(deployment_config, environment, images, env_state)
755
-
756
753
  env_state.save(env_state_path)
757
754
  print(f"Monolithic deployment state saved to {env_state_path}")
758
755
 
@@ -764,7 +761,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
764
761
  """Deploys the application."""
765
762
  env_state_path = self._get_env_state_path(environment.name)
766
763
  env_state = MonolithicDeploymentState.load(env_state_path)
767
- cloud_provider = deployment_config.cloud_provider_instance
764
+ cloud_provider = environment.cloud_provider_instance
768
765
  state_updated = False
769
766
 
770
767
  # Release frontend services
@@ -842,7 +839,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
842
839
  ):
843
840
  """Destroys the environment's infrastructure."""
844
841
  print("\n[bold blue]Destroying monolithic environment...[/bold blue]")
845
- cloud_provider = deployment_config.cloud_provider_instance
842
+ cloud_provider = environment.cloud_provider_instance
846
843
 
847
844
  env_state_path = self._get_env_state_path(environment.name)
848
845
  if not env_state_path.exists():
@@ -963,7 +960,7 @@ class MonolithicDeploymentStrategy(BaseDeploymentStrategy):
963
960
  self.deployments_path
964
961
  / "environments"
965
962
  / "global"
966
- / environment.region
963
+ / f"{cloud_provider.name()}-{environment.region}"
967
964
  / "container_registry"
968
965
  )
969
966
 
@@ -93,5 +93,6 @@ class GitRepo:
93
93
  f.write(ignore_block_str)
94
94
 
95
95
  print(
96
- "[bold green].gitignore has been updated to ignore Terraform state files.[/bold green]"
96
+ "[bold green].gitignore has been updated to ignore Terraform state files.\n[/bold"
97
+ " green]"
97
98
  )
@@ -45,14 +45,12 @@ class BaseInfrastructureProvisioner:
45
45
  # Use shutil.copytree to copy the contents of the template directory
46
46
  shutil.copytree(template_dir, self.working_dir, dirs_exist_ok=True)
47
47
 
48
- print(f"[green]{self.command_name} files copied to: {self.working_dir}[/green]")
48
+ print(f"[grey50]{self.command_name} files copied to: {self.working_dir}[/grey50]")
49
49
 
50
50
  def _run_command(
51
51
  self, command: List[str], env: Optional[Dict[str, str]] = None
52
52
  ) -> Dict[str, str]:
53
53
  """Runs a command and streams its output."""
54
- print(f"\n[bold]Running `{self.command_name}` in {self.working_dir}...[/bold]")
55
-
56
54
  process_env = os.environ.copy()
57
55
  process_env.update(env or {})
58
56
  outputs = {}
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from pathlib import Path
3
- from typing import Annotated, Optional, Type
3
+ from typing import Annotated, Optional, Type, Union
4
4
 
5
5
  import inquirer
6
6
  import logfire
@@ -33,7 +33,7 @@ from opsmith.utils import (
33
33
  slugify,
34
34
  )
35
35
 
36
- app = typer.Typer(pretty_exceptions_show_locals=True)
36
+ app = typer.Typer(pretty_exceptions_show_locals=False)
37
37
 
38
38
 
39
39
  def _check_external_dependencies():
@@ -52,7 +52,7 @@ def _check_external_dependencies():
52
52
  raise typer.Exit(code=1)
53
53
 
54
54
 
55
- def parse_model_arg(model: str) -> Type[BaseAiModel]:
55
+ def _parse_model_arg(model: Union[str, BaseAiModel]) -> BaseAiModel:
56
56
  """
57
57
  Fetches the class corresponding to the given model name.
58
58
 
@@ -69,6 +69,8 @@ def parse_model_arg(model: str) -> Type[BaseAiModel]:
69
69
  :raises ValueError: If the given model name is not found in the model registry.
70
70
  :raises typer.BadParameter: If the provided model name is unsupported.
71
71
  """
72
+ if isinstance(model, BaseAiModel):
73
+ return model
72
74
  try:
73
75
  return MODEL_REGISTRY.get_model_class(model)()
74
76
  except ValueError:
@@ -77,7 +79,7 @@ def parse_model_arg(model: str) -> Type[BaseAiModel]:
77
79
  )
78
80
 
79
81
 
80
- def api_key_callback(ctx: typer.Context, value: str):
82
+ def _api_key_callback(ctx: typer.Context, value: str):
81
83
  """
82
84
  This function serves as a callback for validating and processing an API key when
83
85
  used in conjunction with a command-line interface. The function checks whether
@@ -111,18 +113,21 @@ def main(
111
113
  model: Annotated[
112
114
  Type[BaseAiModel],
113
115
  typer.Option(
114
- parser=parse_model_arg,
116
+ parser=_parse_model_arg,
115
117
  help="The LLM model to be used for by the AI Agent.",
118
+ prompt="Select the LLM model to be used for by the AI Agent",
116
119
  ),
117
120
  ],
118
121
  api_key: Annotated[
119
122
  str,
120
123
  typer.Option(
121
- callback=api_key_callback,
124
+ callback=_api_key_callback,
122
125
  help=(
123
126
  "The API KEY to be used for by the AI Agent. This is the API key for the specified"
124
127
  " model."
125
128
  ),
129
+ prompt="Enter the API KEY for the specified model",
130
+ hide_input=True,
126
131
  ),
127
132
  ],
128
133
  logfire_token: Optional[str] = typer.Option(
@@ -135,6 +140,7 @@ def main(
135
140
  src_dir: Optional[str] = typer.Option(
136
141
  default=None,
137
142
  help="Source directory to be used by the command. Defaults to current working directory.",
143
+ envvar="OPSMITH_SRC_DIR",
138
144
  ),
139
145
  verbose: bool = typer.Option(
140
146
  False, "--verbose", "-v", help="Enable verbose output for repo map generation."
@@ -227,7 +233,7 @@ def setup(ctx: typer.Context):
227
233
  scan_services = True
228
234
 
229
235
  else:
230
- print("No existing deployment configuration found. Starting analysis...")
236
+ print("No existing deployment configuration found. Starting analysis...\n")
231
237
  app_name_questions = [
232
238
  inquirer.Text("app_name", message="Enter the application name"),
233
239
  ]
@@ -237,39 +243,9 @@ def setup(ctx: typer.Context):
237
243
  raise typer.Exit(code=1)
238
244
  app_name = app_name_answers["app_name"]
239
245
 
240
- provider_questions = [
241
- inquirer.List(
242
- "cloud_provider",
243
- message="Select the cloud provider for deployment",
244
- choices=CLOUD_PROVIDER_REGISTRY.choices,
245
- ),
246
- ]
247
- provider_answers = inquirer.prompt(provider_questions)
248
- if not provider_answers:
249
- print("[bold red]Cloud provider selection is required. Aborting.[/bold red]")
250
- raise typer.Exit(code=1)
251
-
252
- selected_provider_value = provider_answers["cloud_provider"]
253
-
254
- # Get cloud details if new or changed
255
- print(f"Initializing {selected_provider_value} provider...")
256
- provider_class = CLOUD_PROVIDER_REGISTRY.get_provider_class(selected_provider_value)
257
- try:
258
- cloud_details = provider_class.get_account_details().model_dump(mode="json")
259
- except CloudCredentialsError as e:
260
- print(f"[bold red]Cloud provider authentication/configuration error:\n{e}[/bold red]")
261
- raise typer.Exit(code=1)
262
- except Exception as e:
263
- print(
264
- "[bold red]An unexpected error occurred while initializing cloud provider or"
265
- f" fetching details: {e}. Aborting.[/bold red]"
266
- )
267
- raise typer.Exit(code=1)
268
-
269
246
  deployment_config = DeploymentConfig(
270
247
  app_name=app_name,
271
248
  app_name_slug=slugify(app_name),
272
- cloud_provider=cloud_details,
273
249
  )
274
250
  scan_services = True
275
251
  git_repo.ensure_gitignore()
@@ -363,10 +339,39 @@ def deploy(ctx: typer.Context):
363
339
  selected_env_name = answers["environment"]
364
340
 
365
341
  if selected_env_name == "<Create a new environment>":
342
+ provider_questions = [
343
+ inquirer.List(
344
+ "cloud_provider",
345
+ message="Select the cloud provider for deployment",
346
+ choices=CLOUD_PROVIDER_REGISTRY.choices,
347
+ ),
348
+ ]
349
+ provider_answers = inquirer.prompt(provider_questions)
350
+ if not provider_answers:
351
+ print("[bold red]Cloud provider selection is required. Aborting.[/bold red]")
352
+ raise typer.Exit(code=1)
353
+
354
+ selected_provider_value = provider_answers["cloud_provider"]
355
+
356
+ # Initialize the provider
357
+ print(f"Initializing {selected_provider_value} provider...\n")
358
+ provider_class = CLOUD_PROVIDER_REGISTRY.get_provider_class(selected_provider_value)
359
+ try:
360
+ cloud_details = provider_class.get_account_details().model_dump(mode="json")
361
+ except CloudCredentialsError as e:
362
+ print(f"[bold red]Cloud provider authentication/configuration error:\n{e}[/bold red]")
363
+ raise typer.Exit(code=1)
364
+ except Exception as e:
365
+ print(
366
+ "[bold red]An unexpected error occurred while initializing cloud provider or"
367
+ f" fetching details: {e}. Aborting.[/bold red]"
368
+ )
369
+ raise typer.Exit(code=1)
370
+
366
371
  # Get cloud provider to fetch regions
367
372
  with WaitingSpinner(text="Fetching regions from your cloud provider"):
368
373
  try:
369
- provider_instance = deployment_config.cloud_provider_instance
374
+ provider_instance = provider_class(cloud_details)
370
375
  regions = provider_instance.get_regions()
371
376
  except CloudCredentialsError as e:
372
377
  print(
@@ -460,6 +465,7 @@ def deploy(ctx: typer.Context):
460
465
 
461
466
  new_env = DeploymentEnvironment(
462
467
  name=selected_env_name,
468
+ cloud_provider=cloud_details,
463
469
  region=selected_region,
464
470
  strategy=selected_strategy,
465
471
  domains=domains,
@@ -101,6 +101,7 @@ class ModelRegistry:
101
101
  """Load built-in models"""
102
102
  for model_cls in [
103
103
  OpenAIGPT41,
104
+ OpenAIGPTO3,
104
105
  AnthropicClaudeSonnet37,
105
106
  AnthropicClaudeSonnet4,
106
107
  GoogleGlaGemini25Pro,
@@ -139,13 +140,13 @@ class OpenAIGPT41(BaseAiModel):
139
140
  @classmethod
140
141
  def get_model_settings(cls) -> ModelSettings:
141
142
  """Returns model-specific settings."""
142
- return OpenAIModelSettings(openai_reasoning_effort="high")
143
+ return OpenAIModelSettings()
143
144
 
144
145
 
145
146
  class OpenAIGPTO3(BaseAiModel):
146
147
  @classmethod
147
148
  def name(cls) -> str:
148
- return "gpt-o3"
149
+ return "o3"
149
150
 
150
151
  @classmethod
151
152
  def provider(cls) -> str:
@@ -70,6 +70,8 @@ service described below.
70
70
  Service Details:
71
71
  {service_info_yaml}
72
72
 
73
+ {template_section}
74
+
73
75
  Repository Map:
74
76
  {repo_map_str}
75
77
 
@@ -88,6 +88,9 @@ class RepoMap:
88
88
  self.src_dir = Path(src_dir).resolve()
89
89
  self.git_repo = GitRepo(self.src_dir)
90
90
  self.verbose = verbose
91
+ self.tracked_files: List[Path] = self.git_repo.get_git_tracked_files(
92
+ [str(self.src_dir), ":!**/*test*"]
93
+ )
91
94
 
92
95
  # Initialize tracking variables
93
96
  self.warned_files = set()
@@ -146,9 +149,7 @@ class RepoMap:
146
149
  if self.max_map_tokens <= 0:
147
150
  return None # Repo map is disabled
148
151
 
149
- all_tracked_files_paths = self.git_repo.get_git_tracked_files(
150
- [str(self.src_dir), ":!**/*test*"]
151
- )
152
+ all_tracked_files_paths = self.tracked_files
152
153
  if not all_tracked_files_paths:
153
154
  if self.verbose:
154
155
  typer.echo("RepoMap: No git-tracked files found.", err=True)