alchemax-plugin 0.1.0__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 (30) hide show
  1. alchemax_plugin-0.1.0/.gitignore +43 -0
  2. alchemax_plugin-0.1.0/Dockerfile +18 -0
  3. alchemax_plugin-0.1.0/PKG-INFO +10 -0
  4. alchemax_plugin-0.1.0/README.md +544 -0
  5. alchemax_plugin-0.1.0/alchemax_plugin/__init__.py +3 -0
  6. alchemax_plugin-0.1.0/alchemax_plugin/analyzer.py +83 -0
  7. alchemax_plugin-0.1.0/alchemax_plugin/builder.py +211 -0
  8. alchemax_plugin-0.1.0/alchemax_plugin/metadata.py +170 -0
  9. alchemax_plugin-0.1.0/alchemax_plugin/runner.py +86 -0
  10. alchemax_plugin-0.1.0/alchemax_plugin/server.py +713 -0
  11. alchemax_plugin-0.1.0/alchemax_plugin/terraform/docker-compose.yml.tpl +23 -0
  12. alchemax_plugin-0.1.0/alchemax_plugin/terraform/ebs.tf +43 -0
  13. alchemax_plugin-0.1.0/alchemax_plugin/terraform/ec2.tf +102 -0
  14. alchemax_plugin-0.1.0/alchemax_plugin/terraform/init.sh +138 -0
  15. alchemax_plugin-0.1.0/alchemax_plugin/terraform/main.tf +42 -0
  16. alchemax_plugin-0.1.0/alchemax_plugin/terraform/network.tf +98 -0
  17. alchemax_plugin-0.1.0/alchemax_plugin/terraform/nginx.conf.tpl +103 -0
  18. alchemax_plugin-0.1.0/alchemax_plugin/terraform/outputs.tf +41 -0
  19. alchemax_plugin-0.1.0/alchemax_plugin/terraform/variables.tf +46 -0
  20. alchemax_plugin-0.1.0/alchemax_plugin/tfstate.py +37 -0
  21. alchemax_plugin-0.1.0/alchemax_plugin/workspace.py +66 -0
  22. alchemax_plugin-0.1.0/api.py +79 -0
  23. alchemax_plugin-0.1.0/opencode.json +11 -0
  24. alchemax_plugin-0.1.0/pyproject.toml +30 -0
  25. alchemax_plugin-0.1.0/tests/__init__.py +1 -0
  26. alchemax_plugin-0.1.0/tests/test_analyzer.py +53 -0
  27. alchemax_plugin-0.1.0/tests/test_builder.py +65 -0
  28. alchemax_plugin-0.1.0/tests/test_metadata.py +177 -0
  29. alchemax_plugin-0.1.0/tests/test_workspace.py +95 -0
  30. alchemax_plugin-0.1.0/uv.lock +1143 -0
@@ -0,0 +1,43 @@
1
+ # Terraform
2
+ *.tfstate*
3
+ .terraform
4
+ .terraform.lock.hcl
5
+ terraform.tfvars.json
6
+ *.pem
7
+
8
+ # Python
9
+ __pycache__
10
+ *.py[cod]
11
+ *$py.class
12
+ *.so
13
+ .Python
14
+ build
15
+ develop-eggs
16
+ dist
17
+ downloads
18
+ eggs
19
+ .eggs
20
+ lib
21
+ lib64
22
+ parts
23
+ sdist
24
+ var
25
+ wheels
26
+ *.egg-info
27
+ .installed.cfg
28
+ *.egg
29
+ .pytest_cache
30
+ .venv
31
+ venv
32
+
33
+ # IDE
34
+ .vscode
35
+ .idea
36
+ *.swp
37
+ *.swo
38
+
39
+ # OS
40
+ .DS_Store
41
+ .env
42
+ .env.local
43
+ *.tar.gz
@@ -0,0 +1,18 @@
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copy project files
6
+ COPY . .
7
+
8
+ # Install dependencies
9
+ RUN pip install --no-cache-dir -e .
10
+
11
+ # Install FastAPI and uvicorn
12
+ RUN pip install --no-cache-dir fastapi uvicorn[standard]
13
+
14
+ # Expose port
15
+ EXPOSE 8000
16
+
17
+ # Run API server
18
+ CMD ["python", "api.py"]
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: alchemax-plugin
3
+ Version: 0.1.0
4
+ Summary: Standalone MCP plugin for deploying Docker apps to AWS EC2
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: boto3>=1.34
7
+ Requires-Dist: fastapi>=0.115
8
+ Requires-Dist: httpx>=0.27
9
+ Requires-Dist: mcp>=1.0
10
+ Requires-Dist: uvicorn[standard]>=0.30
@@ -0,0 +1,544 @@
1
+ # Alchemax Plugin
2
+
3
+ **Standalone Python MCP toolkit for deploying Docker applications to AWS EC2.**
4
+
5
+ A lightweight, infrastructure-as-code plugin that provisions a single EC2 instance per workspace and manages multi-app deployments via docker-compose + Terraform. Zero SaaS, zero dashboard—infrastructure lives in your AWS account.
6
+
7
+ **Use cases:**
8
+ - Deploy multiple Docker services (API, worker, scheduler) to one EC2
9
+ - One-command deploys with dependency ordering
10
+ - Infrastructure as code (Terraform) with optional app manifests (alchemax.json)
11
+ - Local state, no remote backend required
12
+ - API logging & health checks via SSM (no SSH needed)
13
+
14
+ ## Installation
15
+
16
+ ### Claude Code
17
+
18
+ ```bash
19
+ # No install needed — uvx runs it on demand
20
+ claude mcp add alchemax-plugin -- uvx alchemax-plugin
21
+ ```
22
+
23
+ Or install permanently first:
24
+
25
+ ```bash
26
+ uv tool install alchemax-plugin
27
+ claude mcp add alchemax-plugin -- alchemax-plugin
28
+ ```
29
+
30
+ ### Opencode
31
+
32
+ No install needed — use `uvx` directly in your `opencode.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "alchemax-plugin": {
38
+ "command": "uvx",
39
+ "args": ["alchemax-plugin"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ Or install permanently:
46
+
47
+ ```bash
48
+ uv tool install alchemax-plugin
49
+ ```
50
+
51
+ Then add to your project's `opencode.json`:
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "alchemax-plugin": {
57
+ "command": "alchemax-plugin"
58
+ }
59
+ }
60
+ }
61
+ ```
62
+
63
+ Or run directly without installing:
64
+
65
+ ```json
66
+ {
67
+ "mcpServers": {
68
+ "alchemax-plugin": {
69
+ "command": "uvx",
70
+ "args": ["alchemax-plugin"]
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Manual (any MCP client)
77
+
78
+ ```bash
79
+ pip install alchemax-plugin
80
+ # then point your MCP client at: alchemax-plugin
81
+ ```
82
+
83
+ ## Setup
84
+
85
+ ### 1. AWS Credentials
86
+
87
+ ```bash
88
+ aws configure
89
+ # or use AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars
90
+ ```
91
+
92
+ ### 2. Docker Hub (for building images)
93
+
94
+ ```bash
95
+ export DOCKER_HUB_USERNAME=your-username
96
+ docker login
97
+ ```
98
+
99
+ ### 3. Initialize Workspace
100
+
101
+ ```bash
102
+ alchemax-plugin setup prod us-east-1
103
+ # Creates ~/.alp/prod/terraform/ with all infrastructure code
104
+ ```
105
+
106
+ ## Quick Start Examples
107
+
108
+ **Single app from Docker image:**
109
+ ```bash
110
+ alchemax-plugin deploy_app prod myapp docker.io/user/myapp:v1.0.0 8000
111
+ ```
112
+
113
+ **Single app from source (auto-build):**
114
+ ```bash
115
+ alchemax-plugin deploy_from_source prod myapp /path/to/source 8000
116
+ ```
117
+
118
+ **Multi-app project with manifest:**
119
+ ```bash
120
+ # In your project root, create alchemax.json (see below)
121
+ alchemax-plugin deploy_all prod /path/to/project
122
+ ```
123
+
124
+ **List all workspaces:**
125
+ ```bash
126
+ alchemax-plugin list_workspaces
127
+ ```
128
+
129
+ **View instance status & logs:**
130
+ ```bash
131
+ alchemax-plugin get_status prod
132
+ alchemax-plugin get_logs prod backend 50
133
+ ```
134
+
135
+ **Tear down:**
136
+ ```bash
137
+ alchemax-plugin destroy prod
138
+ # EBS volume has prevent_destroy; see gotchas below for removal
139
+ ```
140
+
141
+ ## Project Manifest (alchemax.json)
142
+
143
+ Multi-app projects define an optional `alchemax.json` in the project root. Without it, use `deploy_app()` / `deploy_from_source()` for single apps.
144
+
145
+ ```json
146
+ {
147
+ "name": "cloud-suite",
148
+ "workspace": {
149
+ "region": "us-east-1",
150
+ "instance_type": "t3.micro",
151
+ "ebs_volume_size": 10,
152
+ "env": {
153
+ "LOG_LEVEL": "info",
154
+ "DB_HOST": "localhost"
155
+ }
156
+ },
157
+ "apps": [
158
+ {
159
+ "id": "backend",
160
+ "port": 8000,
161
+ "path": "./api",
162
+ "dockerfile": "Dockerfile",
163
+ "tag": "v1.0.0",
164
+ "health_check": "/api/health",
165
+ "env": {
166
+ "DB_PATH": "/data"
167
+ },
168
+ "secrets": ["DATABASE_URL", "API_KEY", "JWT_SECRET"],
169
+ "depends_on": []
170
+ },
171
+ {
172
+ "id": "worker",
173
+ "port": 8001,
174
+ "path": "./worker",
175
+ "tag": "v1.0.0",
176
+ "health_check": "/health",
177
+ "depends_on": ["backend"]
178
+ }
179
+ ]
180
+ }
181
+ ```
182
+
183
+ ### Schema
184
+
185
+ - **`workspace.env`** — committed env vars (LOG_LEVEL, DB_HOST, etc.). Safe to commit.
186
+ - **`apps[].env`** — per-app committed vars. Merged with workspace.env at deploy.
187
+ - **`apps[].secrets`** — list of secret names. Values pulled from:
188
+ 1. Process environment (`export DATABASE_URL=...`)
189
+ 2. `.env.local` file in project root (git-ignored)
190
+ 3. AWS SSM Parameter Store (future)
191
+
192
+ **Never commit secret values to alchemax.json.**
193
+
194
+ - **`apps[].tag`** — Docker image tag. Not `:latest` (enables rollback + version tracking).
195
+ - **`apps[].depends_on`** — array of app IDs. Deployment order enforced + passed to docker-compose.
196
+ - **`apps[].health_check`** — optional HTTP endpoint for readiness checks (e.g., `/api/health`).
197
+
198
+ ### Secrets Workflow
199
+
200
+ ```bash
201
+ # 1. Define secret names in manifest
202
+ # "secrets": ["DATABASE_URL", "API_KEY"]
203
+
204
+ # 2. Set values locally
205
+ export DATABASE_URL="postgres://localhost/db"
206
+ export API_KEY="secret-value"
207
+
208
+ # 3. Or write .env.local (git-ignored)
209
+ echo "DATABASE_URL=postgres://localhost/db" > .env.local
210
+ echo "API_KEY=secret-value" >> .env.local
211
+
212
+ # 4. Deploy — secrets injected into containers
213
+ alchemax-plugin deploy_all prod /path/to/project
214
+ ```
215
+
216
+ ## Architecture
217
+
218
+ ```
219
+ ┌─────────────────────────────────────────────────────────────────┐
220
+ │ Your Project (git repo) │
221
+ │ ├── api/ (FastAPI, Express, Django, etc.) │
222
+ │ ├── worker/ (Python, Node, Go worker) │
223
+ │ ├── alchemax.json (optional multi-app manifest) │
224
+ │ └── .env.local (git-ignored, secrets) │
225
+ └──────────┬──────────────────────────────────────────────────────┘
226
+ │ alchemax-plugin CLI or MCP
227
+
228
+ ┌──────────────────────────────────────────────────────────────────┐
229
+ │ ~/.alp/<workspace>/ │
230
+ │ ├── terraform/ (Terraform state + .tf files) │
231
+ │ ├── terraform.tfvars.json (generated from apps.json) │
232
+ │ └── apps.json (deployed app list + metadata) │
233
+ └──────────┬──────────────────────────────────────────────────────┘
234
+ │ Terraform Apply
235
+
236
+ ┌──────────────────────────────────────────────────────────────────┐
237
+ │ AWS Account │
238
+ │ ├── EC2 Instance (t3.micro, Ubuntu 22.04) │
239
+ │ │ ├── Docker Daemon │
240
+ │ │ │ ├── Container: backend (port 8000) │
241
+ │ │ │ ├── Container: worker (port 8001) │
242
+ │ │ │ └── ... │
243
+ │ │ ├── nginx reverse proxy (:80, :443) │
244
+ │ │ └── /data/ (EBS volume, prevent_destroy) │
245
+ │ ├── VPC + Security Group (allow 22, 80, 443, app ports) │
246
+ │ ├── EIP (elastic IP for stable public IP) │
247
+ │ ├── IAM Role (SSM access for get_logs) │
248
+ │ └── Route53 (optional custom domain + TLS) │
249
+ └──────────────────────────────────────────────────────────────────┘
250
+ ```
251
+
252
+ One EC2 per workspace. All apps on the same instance via docker-compose.
253
+
254
+ ## Tools Reference
255
+
256
+ ### Discovery
257
+
258
+ **`list_workspaces()`**
259
+ List all workspaces with live AWS EC2 data (state, public IP, app count).
260
+ ```bash
261
+ alchemax-plugin list_workspaces
262
+ ```
263
+
264
+ **`get_project_info(project_path)`**
265
+ Read alchemax.json and return app metadata + deployment order.
266
+ ```bash
267
+ alchemax-plugin get_project_info /path/to/project
268
+ ```
269
+
270
+ ### Setup
271
+
272
+ **`setup(workspace, region?)`**
273
+ Initialize workspace. Creates ~/.alp/<workspace>/terraform/, copies .tf files, runs `terraform init`.
274
+ ```bash
275
+ alchemax-plugin setup prod us-east-1
276
+ alchemax-plugin setup staging eu-west-1
277
+ ```
278
+
279
+ ### Deployment
280
+
281
+ **`deploy_app(workspace, name, image, port, env?)`**
282
+ Deploy a Docker image to the workspace.
283
+ ```bash
284
+ alchemax-plugin deploy_app prod api docker.io/user/myapi:v1.0.0 8000 \
285
+ '{"LOG_LEVEL": "info"}'
286
+ ```
287
+
288
+ **`deploy_from_source(workspace, name, source_path, port?, env?)`**
289
+ Build Docker image from source (auto-detects language), push to Docker Hub, deploy.
290
+ ```bash
291
+ alchemax-plugin deploy_from_source prod backend /path/to/api 8000 \
292
+ '{"DEBUG": "false"}'
293
+ ```
294
+
295
+ **`deploy_all(workspace, project_path, docker_hub_user?)`**
296
+ Deploy all apps from alchemax.json in dependency order. Stops on first failure.
297
+ ```bash
298
+ alchemax-plugin deploy_all prod /path/to/project
299
+ ```
300
+
301
+ ### Status & Logs
302
+
303
+ **`get_status(workspace)`**
304
+ Check EC2 instance state (running/stopped/not-found), public IP, uptime, app count.
305
+ ```bash
306
+ alchemax-plugin get_status prod
307
+ ```
308
+
309
+ **`get_logs(workspace, app_name, lines?)`**
310
+ Fetch container logs via AWS SSM SendCommand (no SSH required).
311
+ ```bash
312
+ alchemax-plugin get_logs prod backend 100
313
+ alchemax-plugin get_logs prod worker 50
314
+ ```
315
+
316
+ **`list_apps(workspace)`**
317
+ List deployed apps in a workspace.
318
+ ```bash
319
+ alchemax-plugin list_apps prod
320
+ ```
321
+
322
+ ### Management
323
+
324
+ **`remove_app(workspace, name)`**
325
+ Remove an app from the workspace and re-apply Terraform.
326
+ ```bash
327
+ alchemax-plugin remove_app prod worker
328
+ ```
329
+
330
+ **`configure_domain(workspace, domain)`**
331
+ Point custom domain at the workspace EC2 instance (Route 53 + certbot TLS).
332
+ ```bash
333
+ alchemax-plugin configure_domain prod myapp.example.com
334
+ ```
335
+
336
+ **`destroy(workspace)`**
337
+ Tear down all infrastructure (EC2, VPC, EBS, IAM). **EBS volume has `prevent_destroy` to guard against data loss.**
338
+ ```bash
339
+ alchemax-plugin destroy prod
340
+ # If destroy fails due to prevent_destroy:
341
+ # terraform state rm aws_ebs_volume.data
342
+ # alchemax-plugin destroy prod
343
+ ```
344
+
345
+ ## IAM Policy
346
+
347
+ Attach this policy to your AWS user. Minimal permissions for alchemax-plugin:
348
+
349
+ ```json
350
+ {
351
+ "Version": "2012-10-17",
352
+ "Statement": [
353
+ {
354
+ "Effect": "Allow",
355
+ "Action": [
356
+ "ec2:*",
357
+ "iam:CreateRole",
358
+ "iam:CreateInstanceProfile",
359
+ "iam:AttachRolePolicy",
360
+ "iam:AddRoleToInstanceProfile",
361
+ "iam:GetRole",
362
+ "iam:GetInstanceProfile",
363
+ "iam:DeleteRole",
364
+ "iam:DeleteInstanceProfile",
365
+ "iam:RemoveRoleFromInstanceProfile",
366
+ "iam:DetachRolePolicy",
367
+ "iam:ListInstanceProfiles"
368
+ ],
369
+ "Resource": "*"
370
+ }
371
+ ]
372
+ }
373
+ ```
374
+
375
+ ## MCP Integration
376
+
377
+ Use as an MCP server in Claude Desktop or other MCP clients:
378
+
379
+ ```json
380
+ // ~/.claude/mcp_servers.json
381
+ {
382
+ "alchemax-plugin": {
383
+ "command": "python",
384
+ "args": ["-m", "alchemax_plugin.server"]
385
+ }
386
+ }
387
+ ```
388
+
389
+ Then call tools via MCP:
390
+ ```
391
+ Claude: deploy_all workspace=prod project_path=/path/to/project
392
+ ```
393
+
394
+ ## State & Data Persistence
395
+
396
+ **State:**
397
+ - `~/.alp/<workspace>/terraform/` — Terraform state (local, not remote)
398
+ - `~/.alp/<workspace>/apps.json` — deployed app list + metadata
399
+ - `~/.alp/<workspace>/terraform.tfvars.json` — generated from apps.json
400
+
401
+ **Data:**
402
+ - `/data/<app-name>/` — EBS volume mounted in each container
403
+ - Persists across container restarts and even workspace teardown (prevent_destroy)
404
+ - To delete data: manually delete EBS volume in AWS console or via CLI
405
+
406
+ ## Troubleshooting
407
+
408
+ ### "Docker buildx: permission denied"
409
+ ```bash
410
+ docker logout
411
+ docker login
412
+ # Re-run deploy
413
+ ```
414
+
415
+ ### "Terraform state is locked"
416
+ ```bash
417
+ # Find the lock table name in ~/.alp/<workspace>/terraform/
418
+ aws dynamodb delete-item \
419
+ --table-name alchemax-tflock-xxxxx \
420
+ --key '{"LockID": {"S": "..."}}'
421
+ ```
422
+
423
+ ### "EBS volume has prevent_destroy"
424
+ ```bash
425
+ cd ~/.alp/<workspace>/terraform/
426
+ terraform state rm aws_ebs_volume.data
427
+ terraform destroy # or use CLI: alchemax-plugin destroy <workspace>
428
+ ```
429
+
430
+ ### Instance not responding / containers failing
431
+ ```bash
432
+ alchemax-plugin get_logs prod backend 100
433
+ alchemax-plugin get_status prod # check EC2 state
434
+ # SSH if needed:
435
+ ssh -i ~/.alp/<workspace>/terraform/alp-key.pem ubuntu@<public-ip>
436
+ ```
437
+
438
+ ### App not reachable at public IP
439
+ 1. Check instance is running: `get_status`
440
+ 2. Check nginx routing: `get_logs prod` (look for 404 errors)
441
+ 3. Verify port mapping in `list_apps`
442
+ 4. Check docker-compose: SSH to instance, run `docker ps`
443
+
444
+ ### Env vars not set in container
445
+ ```bash
446
+ # Verify in deployed apps.json
447
+ cat ~/.alp/<workspace>/apps.json
448
+ # If missing, re-deploy:
449
+ alchemax-plugin deploy_app <workspace> <app> <image> <port> '<env-json>'
450
+ ```
451
+
452
+ ## Known Limitations
453
+
454
+ - **Docker-only** — no Lambda, Cloud Run, or other runtimes. Cross-provider orchestration belongs in your orchestrator repo.
455
+ - **Single EC2 per workspace** — all apps share the same instance (cost-efficient for small teams, not multi-tenant).
456
+ - **No built-in monitoring** — hook it up to your own CloudWatch / Prometheus.
457
+ - **`:latest` images** — tag images explicitly in manifests for rollback safety.
458
+ - **No auto-scaling** — static instance. For variable load, manually scale up instance type.
459
+
460
+ ## Development
461
+
462
+ ### Running Tests
463
+
464
+ ```bash
465
+ source .venv/bin/activate
466
+ pytest tests/ -v
467
+ ```
468
+
469
+ ### Project Structure
470
+
471
+ ```
472
+ alchemax_plugin/
473
+ ├── server.py # MCP server + tool definitions (12 tools)
474
+ ├── metadata.py # alchemax.json parsing + validation
475
+ ├── builder.py # docker buildx + registry push
476
+ ├── runner.py # Terraform CLI wrapper
477
+ ├── workspace.py # ~/.alp/<workspace> state management
478
+ ├── tfstate.py # apps.json ↔ terraform.tfvars conversion
479
+ ├── analyzer.py # language/framework detection
480
+ └── terraform/ # Terraform modules (6 .tf files + 2 templates)
481
+ ├── main.tf # Providers + locals
482
+ ├── variables.tf # All configurable vars
483
+ ├── network.tf # VPC, subnet, security group
484
+ ├── ec2.tf # EC2 instance + IAM role
485
+ ├── ebs.tf # EBS volume + SSH key
486
+ ├── outputs.tf # Terraform outputs
487
+ ├── docker-compose.yml.tpl
488
+ ├── nginx.conf.tpl
489
+ └── init.sh # EC2 user-data script
490
+
491
+ tests/
492
+ ├── test_metadata.py # 11 tests: parsing, validation, ordering
493
+ ├── test_workspace.py # 4 tests: state management
494
+ ├── test_builder.py # 4 tests: Docker image analysis
495
+ └── test_analyzer.py # 4 tests: language detection
496
+ ```
497
+
498
+ ### Adding a New Tool
499
+
500
+ 1. Add `@register_tool()` decorator with schema
501
+ 2. Implement function in `server.py`
502
+ 3. Add test in `tests/test_*.py`
503
+ 4. Update README
504
+
505
+ Example:
506
+ ```python
507
+ @register_tool(
508
+ "my_tool",
509
+ "Description of what it does.",
510
+ {
511
+ "type": "object",
512
+ "properties": {
513
+ "workspace": {"type": "string"},
514
+ },
515
+ "required": ["workspace"],
516
+ },
517
+ )
518
+ def my_tool(workspace: str) -> dict[str, Any]:
519
+ """Implementation."""
520
+ return {"success": True, "result": "..."}
521
+ ```
522
+
523
+ ### Modifying Terraform
524
+
525
+ Terraform files are ported as-is from alchemax. Key constraints:
526
+ - Local state only (no S3 backend)
527
+ - alp- prefix for all IAM roles (avoid collisions)
528
+ - docker-compose on t3.micro (adjust variables.tf for larger instances)
529
+ - EBS prevent_destroy (data safety)
530
+ - nginx reverse proxy on ports 80/443
531
+
532
+ Changes to `.tf` files are picked up on next `deploy_all()` or `setup()`.
533
+
534
+ ## Contributing
535
+
536
+ Contributions welcome. Maintain the "primitive, not platform" philosophy:
537
+ - Keep it Docker-only
538
+ - No cross-provider types
539
+ - One EC2 per workspace
540
+ - Keep state local (no remote backend)
541
+
542
+ ## License
543
+
544
+ MIT
@@ -0,0 +1,3 @@
1
+ """Alchemax plugin: standalone MCP toolkit for Docker app deployment to AWS EC2."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,83 @@
1
+ """Source code language and framework detection."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+
8
+ class SourceAnalyzer:
9
+ """Analyzes source code for language, framework, and Dockerfile presence."""
10
+
11
+ @staticmethod
12
+ def analyze(source_path: Path) -> dict:
13
+ """Analyze source directory. Return lang, framework, has_dockerfile."""
14
+ if not source_path.exists():
15
+ return {"language": None, "framework": None, "has_dockerfile": False}
16
+
17
+ has_dockerfile = (source_path / "Dockerfile").exists()
18
+ if has_dockerfile:
19
+ return {"language": None, "framework": None, "has_dockerfile": True}
20
+
21
+ language, framework = None, None
22
+
23
+ # Check Node.js
24
+ pkg_json = source_path / "package.json"
25
+ if pkg_json.exists():
26
+ try:
27
+ with open(pkg_json) as f:
28
+ data = json.load(f)
29
+ deps = {**data.get("dependencies", {}), **data.get("devDependencies", {})}
30
+ if "next" in deps:
31
+ language, framework = "node", "next"
32
+ elif "express" in deps:
33
+ language, framework = "node", "express"
34
+ elif "react" in deps:
35
+ language, framework = "node", "react"
36
+ else:
37
+ language = "node"
38
+ except (json.JSONDecodeError, OSError):
39
+ language = "node"
40
+
41
+ # Check Python
42
+ if not language:
43
+ if (source_path / "requirements.txt").exists():
44
+ language = "python"
45
+ try:
46
+ with open(source_path / "requirements.txt") as f:
47
+ content = f.read()
48
+ if "fastapi" in content:
49
+ framework = "fastapi"
50
+ elif "flask" in content:
51
+ framework = "flask"
52
+ elif "django" in content:
53
+ framework = "django"
54
+ except OSError:
55
+ pass
56
+
57
+ elif (source_path / "pyproject.toml").exists():
58
+ language = "python"
59
+ try:
60
+ with open(source_path / "pyproject.toml") as f:
61
+ content = f.read()
62
+ if "fastapi" in content:
63
+ framework = "fastapi"
64
+ elif "flask" in content:
65
+ framework = "flask"
66
+ elif "django" in content:
67
+ framework = "django"
68
+ except OSError:
69
+ pass
70
+
71
+ # Check Go
72
+ if not language and (source_path / "go.mod").exists():
73
+ language = "go"
74
+
75
+ # Check Rust
76
+ if not language and (source_path / "Cargo.toml").exists():
77
+ language = "rust"
78
+
79
+ return {
80
+ "language": language,
81
+ "framework": framework,
82
+ "has_dockerfile": has_dockerfile,
83
+ }