api-medic 1.0.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 (135) hide show
  1. api_medic-1.0.0/.gitattributes +23 -0
  2. api_medic-1.0.0/.github/workflows/ci.yml +139 -0
  3. api_medic-1.0.0/.github/workflows/deploy-demo.yml +103 -0
  4. api_medic-1.0.0/.github/workflows/publish-pypi.yml +78 -0
  5. api_medic-1.0.0/.gitignore +85 -0
  6. api_medic-1.0.0/CHANGELOG.md +78 -0
  7. api_medic-1.0.0/CLAUDE.md +62 -0
  8. api_medic-1.0.0/LICENSE +21 -0
  9. api_medic-1.0.0/Makefile +52 -0
  10. api_medic-1.0.0/PKG-INFO +135 -0
  11. api_medic-1.0.0/README.md +75 -0
  12. api_medic-1.0.0/deploy/README.md +111 -0
  13. api_medic-1.0.0/deploy/iam/inline-policy.json +247 -0
  14. api_medic-1.0.0/deploy/iam/trust-policy.json +19 -0
  15. api_medic-1.0.0/deploy/lambda/Makefile +16 -0
  16. api_medic-1.0.0/deploy/lambda/handler.py +173 -0
  17. api_medic-1.0.0/deploy/lambda/requirements.txt +17 -0
  18. api_medic-1.0.0/deploy/pypi-trusted-publishing.md +89 -0
  19. api_medic-1.0.0/deploy/samconfig.toml +12 -0
  20. api_medic-1.0.0/deploy/template.yaml +237 -0
  21. api_medic-1.0.0/docs/api-medic-ui-reference.html +281 -0
  22. api_medic-1.0.0/docs/architecture.md +511 -0
  23. api_medic-1.0.0/docs/examples/www.markandrewmarquez.com.har +4065 -0
  24. api_medic-1.0.0/extension/.gitkeep +0 -0
  25. api_medic-1.0.0/frontend/.gitignore +10 -0
  26. api_medic-1.0.0/frontend/index.html +12 -0
  27. api_medic-1.0.0/frontend/package-lock.json +4285 -0
  28. api_medic-1.0.0/frontend/package.json +33 -0
  29. api_medic-1.0.0/frontend/postcss.config.js +6 -0
  30. api_medic-1.0.0/frontend/src/App.test.tsx +104 -0
  31. api_medic-1.0.0/frontend/src/App.tsx +82 -0
  32. api_medic-1.0.0/frontend/src/components/FixtureBrowser.tsx +80 -0
  33. api_medic-1.0.0/frontend/src/components/HarUpload.test.tsx +100 -0
  34. api_medic-1.0.0/frontend/src/components/HarUpload.tsx +187 -0
  35. api_medic-1.0.0/frontend/src/components/ReportView.test.tsx +109 -0
  36. api_medic-1.0.0/frontend/src/components/ReportView.tsx +66 -0
  37. api_medic-1.0.0/frontend/src/components/RequestComposer.test.tsx +77 -0
  38. api_medic-1.0.0/frontend/src/components/RequestComposer.tsx +161 -0
  39. api_medic-1.0.0/frontend/src/components/Tabs.tsx +44 -0
  40. api_medic-1.0.0/frontend/src/components/report/FindingCard.tsx +43 -0
  41. api_medic-1.0.0/frontend/src/components/report/HeaderBar.tsx +33 -0
  42. api_medic-1.0.0/frontend/src/components/report/MetricsGrid.tsx +83 -0
  43. api_medic-1.0.0/frontend/src/components/report/RequestLine.tsx +38 -0
  44. api_medic-1.0.0/frontend/src/components/report/SeverityPill.tsx +23 -0
  45. api_medic-1.0.0/frontend/src/components/report/TimingWaterfall.tsx +54 -0
  46. api_medic-1.0.0/frontend/src/index.css +14 -0
  47. api_medic-1.0.0/frontend/src/lib/api.test.ts +81 -0
  48. api_medic-1.0.0/frontend/src/lib/api.ts +89 -0
  49. api_medic-1.0.0/frontend/src/lib/fixtures-contract.test.ts +53 -0
  50. api_medic-1.0.0/frontend/src/lib/fixtures.test.ts +69 -0
  51. api_medic-1.0.0/frontend/src/lib/fixtures.ts +27 -0
  52. api_medic-1.0.0/frontend/src/lib/harStrip.test.ts +138 -0
  53. api_medic-1.0.0/frontend/src/lib/harStrip.ts +143 -0
  54. api_medic-1.0.0/frontend/src/lib/types.ts +110 -0
  55. api_medic-1.0.0/frontend/src/main.tsx +13 -0
  56. api_medic-1.0.0/frontend/src/test/setup.ts +1 -0
  57. api_medic-1.0.0/frontend/src/vite-env.d.ts +1 -0
  58. api_medic-1.0.0/frontend/tailwind.config.js +35 -0
  59. api_medic-1.0.0/frontend/tsconfig.json +27 -0
  60. api_medic-1.0.0/frontend/tsconfig.node.json +15 -0
  61. api_medic-1.0.0/frontend/tsconfig.node.tsbuildinfo +1 -0
  62. api_medic-1.0.0/frontend/tsconfig.tsbuildinfo +1 -0
  63. api_medic-1.0.0/frontend/vite.config.d.ts +2 -0
  64. api_medic-1.0.0/frontend/vite.config.js +79 -0
  65. api_medic-1.0.0/frontend/vite.config.ts +85 -0
  66. api_medic-1.0.0/pyproject.toml +128 -0
  67. api_medic-1.0.0/src/api_medic/__init__.py +3 -0
  68. api_medic-1.0.0/src/api_medic/cli/__init__.py +1 -0
  69. api_medic-1.0.0/src/api_medic/cli/main.py +306 -0
  70. api_medic-1.0.0/src/api_medic/core/__init__.py +23 -0
  71. api_medic-1.0.0/src/api_medic/core/captured.py +94 -0
  72. api_medic-1.0.0/src/api_medic/core/checks/__init__.py +52 -0
  73. api_medic-1.0.0/src/api_medic/core/checks/auth.py +248 -0
  74. api_medic-1.0.0/src/api_medic/core/checks/body.py +139 -0
  75. api_medic-1.0.0/src/api_medic/core/checks/http.py +208 -0
  76. api_medic-1.0.0/src/api_medic/core/checks/network.py +215 -0
  77. api_medic-1.0.0/src/api_medic/core/checks/rate_limit.py +129 -0
  78. api_medic-1.0.0/src/api_medic/core/engine.py +72 -0
  79. api_medic-1.0.0/src/api_medic/core/export_schema.py +24 -0
  80. api_medic-1.0.0/src/api_medic/core/models.py +139 -0
  81. api_medic-1.0.0/src/api_medic/core/parser.py +141 -0
  82. api_medic-1.0.0/src/api_medic/core/render/__init__.py +8 -0
  83. api_medic-1.0.0/src/api_medic/core/render/html.py +205 -0
  84. api_medic-1.0.0/src/api_medic/core/render/json.py +14 -0
  85. api_medic-1.0.0/src/api_medic/core/render/markdown.py +136 -0
  86. api_medic-1.0.0/src/api_medic/core/render/terminal.py +159 -0
  87. api_medic-1.0.0/src/api_medic/core/runner.py +248 -0
  88. api_medic-1.0.0/src/api_medic/core/runner_safety.py +97 -0
  89. api_medic-1.0.0/src/api_medic/web/__init__.py +1 -0
  90. api_medic-1.0.0/src/api_medic/web/__main__.py +5 -0
  91. api_medic-1.0.0/src/api_medic/web/app.py +87 -0
  92. api_medic-1.0.0/src/api_medic/web/frontend/assets/index-B9VfTj3I.css +1 -0
  93. api_medic-1.0.0/src/api_medic/web/frontend/assets/index-DdtPIxCH.js +40 -0
  94. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/01-healthy.json +49 -0
  95. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/02-jwt-expired.json +62 -0
  96. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/03-rate-limited.json +53 -0
  97. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/04-cors-misconfigured.json +63 -0
  98. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/05-tls-expiring.json +50 -0
  99. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/06-malformed-body.json +52 -0
  100. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/07-redirect-loop.json +42 -0
  101. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/08-slow-tls.json +60 -0
  102. api_medic-1.0.0/src/api_medic/web/frontend/fixtures/index.json +1 -0
  103. api_medic-1.0.0/src/api_medic/web/frontend/index.html +13 -0
  104. api_medic-1.0.0/src/api_medic/web/server.py +25 -0
  105. api_medic-1.0.0/tests/__init__.py +0 -0
  106. api_medic-1.0.0/tests/fixtures/reports/01-healthy.json +49 -0
  107. api_medic-1.0.0/tests/fixtures/reports/02-jwt-expired.json +62 -0
  108. api_medic-1.0.0/tests/fixtures/reports/03-rate-limited.json +53 -0
  109. api_medic-1.0.0/tests/fixtures/reports/04-cors-misconfigured.json +63 -0
  110. api_medic-1.0.0/tests/fixtures/reports/05-tls-expiring.json +50 -0
  111. api_medic-1.0.0/tests/fixtures/reports/06-malformed-body.json +52 -0
  112. api_medic-1.0.0/tests/fixtures/reports/07-redirect-loop.json +42 -0
  113. api_medic-1.0.0/tests/fixtures/reports/08-slow-tls.json +60 -0
  114. api_medic-1.0.0/tests/integration/__init__.py +0 -0
  115. api_medic-1.0.0/tests/integration/test_engine_live.py +64 -0
  116. api_medic-1.0.0/tests/unit/__init__.py +0 -0
  117. api_medic-1.0.0/tests/unit/test_checks_auth.py +259 -0
  118. api_medic-1.0.0/tests/unit/test_checks_body.py +175 -0
  119. api_medic-1.0.0/tests/unit/test_checks_http.py +246 -0
  120. api_medic-1.0.0/tests/unit/test_checks_network.py +227 -0
  121. api_medic-1.0.0/tests/unit/test_checks_rate_limit.py +153 -0
  122. api_medic-1.0.0/tests/unit/test_checks_registry.py +76 -0
  123. api_medic-1.0.0/tests/unit/test_cli.py +347 -0
  124. api_medic-1.0.0/tests/unit/test_engine.py +73 -0
  125. api_medic-1.0.0/tests/unit/test_engine_smoke.py +81 -0
  126. api_medic-1.0.0/tests/unit/test_fixtures.py +80 -0
  127. api_medic-1.0.0/tests/unit/test_lambda_handler.py +168 -0
  128. api_medic-1.0.0/tests/unit/test_lambda_imports.py +172 -0
  129. api_medic-1.0.0/tests/unit/test_models.py +197 -0
  130. api_medic-1.0.0/tests/unit/test_parser.py +148 -0
  131. api_medic-1.0.0/tests/unit/test_render_json.py +58 -0
  132. api_medic-1.0.0/tests/unit/test_renderers.py +183 -0
  133. api_medic-1.0.0/tests/unit/test_runner.py +415 -0
  134. api_medic-1.0.0/tests/unit/test_runner_safety.py +122 -0
  135. api_medic-1.0.0/tests/unit/test_web_app.py +146 -0
@@ -0,0 +1,23 @@
1
+ * text=auto eol=lf
2
+ *.py text eol=lf
3
+ *.toml text eol=lf
4
+ *.yml text eol=lf
5
+ *.yaml text eol=lf
6
+ *.json text eol=lf
7
+ *.md text eol=lf
8
+ *.sh text eol=lf
9
+ *.ts text eol=lf
10
+ *.tsx text eol=lf
11
+ *.js text eol=lf
12
+ *.jsx text eol=lf
13
+ *.css text eol=lf
14
+ *.html text eol=lf
15
+ Makefile text eol=lf
16
+ LICENSE text eol=lf
17
+ *.png binary
18
+ *.jpg binary
19
+ *.gif binary
20
+ *.ico binary
21
+ *.pdf binary
22
+ *.zip binary
23
+ *.tar.gz binary
@@ -0,0 +1,139 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest, windows-latest]
16
+ python-version: ["3.10", "3.11", "3.12"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+ cache: pip
26
+
27
+ - name: Install dependencies
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ pip install -e ".[dev]"
31
+
32
+ - name: Lint with ruff
33
+ run: ruff check .
34
+
35
+ - name: Format check with ruff
36
+ run: ruff format --check .
37
+
38
+ - name: Type-check with mypy
39
+ run: mypy src
40
+
41
+ - name: Run tests
42
+ run: pytest -m "not integration" --cov --cov-report=term-missing
43
+
44
+ checks-coverage:
45
+ name: 80% coverage gate on core/checks
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+
50
+ - uses: actions/setup-python@v5
51
+ with:
52
+ python-version: "3.12"
53
+ cache: pip
54
+
55
+ - name: Install dependencies
56
+ run: |
57
+ python -m pip install --upgrade pip
58
+ pip install -e ".[dev]"
59
+
60
+ - name: Coverage gate (core/checks ≥ 80%)
61
+ run: pytest -m "not integration" --cov=src/api_medic/core/checks --cov-report=term-missing --cov-fail-under=80
62
+
63
+ integration:
64
+ name: Integration tests (live, flake-tolerant)
65
+ runs-on: ubuntu-latest
66
+ continue-on-error: true
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+
70
+ - uses: actions/setup-python@v5
71
+ with:
72
+ python-version: "3.12"
73
+ cache: pip
74
+
75
+ - name: Install dependencies
76
+ run: |
77
+ python -m pip install --upgrade pip
78
+ pip install -e ".[dev]"
79
+
80
+ - name: Run integration tests
81
+ run: pytest -m integration -v
82
+
83
+ frontend:
84
+ name: Frontend (typecheck, test, build)
85
+ runs-on: ubuntu-latest
86
+ defaults:
87
+ run:
88
+ working-directory: frontend
89
+ steps:
90
+ - uses: actions/checkout@v4
91
+
92
+ - uses: actions/setup-node@v4
93
+ with:
94
+ node-version: "20"
95
+ cache: npm
96
+ cache-dependency-path: frontend/package-lock.json
97
+
98
+ - name: Install dependencies
99
+ run: npm ci
100
+
101
+ - name: Type-check
102
+ run: npm run typecheck
103
+
104
+ - name: Run tests
105
+ run: npm test
106
+
107
+ - name: Build
108
+ run: npm run build
109
+
110
+ schema-check:
111
+ name: Verify TypeScript types are in sync with Pydantic schema
112
+ runs-on: ubuntu-latest
113
+ steps:
114
+ - uses: actions/checkout@v4
115
+
116
+ - uses: actions/setup-python@v5
117
+ with:
118
+ python-version: "3.12"
119
+ cache: pip
120
+
121
+ - uses: actions/setup-node@v4
122
+ with:
123
+ node-version: "20"
124
+
125
+ - name: Install Python dependencies
126
+ run: |
127
+ python -m pip install --upgrade pip
128
+ pip install -e .
129
+
130
+ - name: Regenerate TypeScript types
131
+ run: make types
132
+
133
+ - name: Fail if generated types changed
134
+ run: |
135
+ if ! git diff --exit-code frontend/src/lib/types.ts; then
136
+ echo "::error::Generated TypeScript types are out of sync."
137
+ echo "::error::Run 'make types' locally and commit the result."
138
+ exit 1
139
+ fi
@@ -0,0 +1,103 @@
1
+ name: Deploy hosted demo
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ id-token: write # for OIDC role assumption
10
+ contents: read
11
+
12
+ jobs:
13
+ deploy:
14
+ name: Deploy api-medic-demo to AWS
15
+ runs-on: ubuntu-latest
16
+ # Repo variable DEMO_DEPLOY_ENABLED gates this — set it to 'true' in
17
+ # repo Settings → Variables once AWS prerequisites are in place
18
+ # (see deploy/README.md). Manual dispatch always runs so it can be
19
+ # tested before flipping the gate.
20
+ if: vars.DEMO_DEPLOY_ENABLED == 'true' || github.event_name == 'workflow_dispatch'
21
+ env:
22
+ AWS_REGION: us-east-1
23
+ STACK_NAME: api-medic-demo
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Configure AWS credentials (OIDC)
28
+ uses: aws-actions/configure-aws-credentials@v4
29
+ with:
30
+ role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
31
+ aws-region: ${{ env.AWS_REGION }}
32
+
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v5
35
+ with:
36
+ python-version: "3.12"
37
+
38
+ - name: Set up Node
39
+ uses: actions/setup-node@v4
40
+ with:
41
+ node-version: "20"
42
+ cache: npm
43
+ cache-dependency-path: frontend/package-lock.json
44
+
45
+ - name: Install AWS SAM CLI
46
+ uses: aws-actions/setup-sam@v2
47
+ with:
48
+ use-installer: true
49
+ token: ${{ secrets.GITHUB_TOKEN }}
50
+
51
+ - name: Build frontend (captured-mode)
52
+ working-directory: frontend
53
+ env:
54
+ VITE_DEMO_MODE: "1"
55
+ run: |
56
+ npm ci
57
+ npm run build
58
+
59
+ - name: Vendor api_medic into Lambda build context
60
+ run: cp -r src/api_medic deploy/lambda/api_medic
61
+
62
+ - name: SAM build
63
+ working-directory: deploy
64
+ run: sam build
65
+
66
+ - name: SAM deploy
67
+ working-directory: deploy
68
+ run: |
69
+ sam deploy \
70
+ --no-confirm-changeset \
71
+ --no-fail-on-empty-changeset \
72
+ --parameter-overrides \
73
+ HostedZoneId=${{ secrets.ROUTE53_HOSTED_ZONE_ID }} \
74
+ CertificateArn=${{ secrets.ACM_CERTIFICATE_ARN }}
75
+
76
+ - name: Read stack outputs
77
+ id: outputs
78
+ run: |
79
+ BUCKET=$(aws cloudformation describe-stacks \
80
+ --stack-name "$STACK_NAME" \
81
+ --query 'Stacks[0].Outputs[?OutputKey==`WebBucketName`].OutputValue' \
82
+ --output text)
83
+ DIST=$(aws cloudformation describe-stacks \
84
+ --stack-name "$STACK_NAME" \
85
+ --query 'Stacks[0].Outputs[?OutputKey==`DistributionId`].OutputValue' \
86
+ --output text)
87
+ echo "bucket=$BUCKET" >> "$GITHUB_OUTPUT"
88
+ echo "distribution=$DIST" >> "$GITHUB_OUTPUT"
89
+
90
+ - name: Sync frontend to S3
91
+ run: |
92
+ aws s3 sync frontend/dist/ "s3://${{ steps.outputs.outputs.bucket }}/" \
93
+ --delete
94
+
95
+ - name: Invalidate CloudFront
96
+ run: |
97
+ aws cloudfront create-invalidation \
98
+ --distribution-id "${{ steps.outputs.outputs.distribution }}" \
99
+ --paths "/*"
100
+
101
+ - name: Print public URL
102
+ run: |
103
+ echo "::notice::Deployed to https://api-medic.markandrewmarquez.com"
@@ -0,0 +1,78 @@
1
+ name: Publish to PyPI
2
+
3
+ # Fires on every v* tag push (e.g. v1.0.0, v1.1.0, v2.0.0-rc1) and via
4
+ # manual dispatch — useful to publish an existing tag without re-tagging.
5
+ #
6
+ # Trusted publishing: no API token in repo secrets. PyPI verifies the
7
+ # upload via an OIDC token that GitHub mints for this workflow. The
8
+ # binding is configured once in PyPI's web UI under Account → Publishing.
9
+ # See deploy/pypi-trusted-publishing.md for the exact one-time steps.
10
+
11
+ on:
12
+ push:
13
+ tags:
14
+ - "v*"
15
+ workflow_dispatch:
16
+ inputs:
17
+ ref:
18
+ description: "Tag or branch to publish from (e.g. v1.0.0)"
19
+ required: true
20
+ default: "v1.0.0"
21
+
22
+ jobs:
23
+ build-and-publish:
24
+ name: Build wheel + sdist and publish to PyPI
25
+ runs-on: ubuntu-latest
26
+ environment: pypi
27
+ permissions:
28
+ id-token: write # required for trusted publishing
29
+ contents: read
30
+ steps:
31
+ - name: Checkout
32
+ uses: actions/checkout@v4
33
+ with:
34
+ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
35
+
36
+ - name: Set up Python
37
+ uses: actions/setup-python@v5
38
+ with:
39
+ python-version: "3.12"
40
+
41
+ - name: Set up Node
42
+ uses: actions/setup-node@v4
43
+ with:
44
+ node-version: "20"
45
+ cache: npm
46
+ cache-dependency-path: frontend/package-lock.json
47
+
48
+ - name: Build frontend bundle
49
+ # Local-web-UI build (no VITE_DEMO_MODE) — the wheel ships the
50
+ # full-feature UI; the demo-mode build is hosted-demo-only.
51
+ working-directory: frontend
52
+ run: |
53
+ npm ci
54
+ npm run build
55
+
56
+ - name: Stage frontend into wheel source tree
57
+ run: |
58
+ # pyproject.toml force-includes src/api_medic/web/frontend/ → wheel.
59
+ # That dir is gitignored at runtime; populate it from the build.
60
+ rm -f src/api_medic/web/frontend/.gitkeep
61
+ cp -r frontend/dist/* src/api_medic/web/frontend/
62
+
63
+ - name: Build wheel and sdist
64
+ run: |
65
+ python -m pip install --upgrade pip build
66
+ python -m build
67
+
68
+ - name: Smoke-check the built distributions
69
+ run: |
70
+ ls -lh dist/
71
+ python -m pip install dist/*.whl
72
+ python -c "import api_medic; print('imported api_medic', api_medic.__name__)"
73
+ api-medic --help | head -10
74
+
75
+ - name: Publish to PyPI
76
+ uses: pypa/gh-action-pypi-publish@release/v1
77
+ # No `with:` block — trusted publishing reads the project name
78
+ # from the wheel metadata and verifies via OIDC.
@@ -0,0 +1,85 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ /build/
8
+ /develop-eggs/
9
+ /dist/
10
+ /downloads/
11
+ /eggs/
12
+ /.eggs/
13
+ /lib/
14
+ /lib64/
15
+ /parts/
16
+ /sdist/
17
+ /var/
18
+ /wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+
24
+ # Virtual environments
25
+ .venv/
26
+ venv/
27
+ env/
28
+ ENV/
29
+
30
+ # Testing
31
+ .coverage
32
+ .coverage.*
33
+ htmlcov/
34
+ .pytest_cache/
35
+ .mypy_cache/
36
+ .ruff_cache/
37
+ coverage.xml
38
+ *.cover
39
+
40
+ # IDE / agent state
41
+ .idea/
42
+ .vscode/
43
+ .claude/
44
+ *.swp
45
+ *.swo
46
+ .DS_Store
47
+
48
+ # Node / frontend
49
+ node_modules/
50
+ frontend/dist/
51
+ frontend/.vite/
52
+ *.log
53
+ .npm/
54
+
55
+ # Built frontend, shipped in wheel — populated at build time, not checked in.
56
+ # .gitkeep keeps the dir in tree so the wheel build's force-include works
57
+ # before Phase 2 has anything to bundle.
58
+ src/api_medic/web/frontend/*
59
+ !src/api_medic/web/frontend/.gitkeep
60
+
61
+ # AWS / SAM
62
+ .aws-sam/
63
+ samconfig.toml.local
64
+ packaged.yaml
65
+
66
+ # Secrets — never commit these
67
+ .env
68
+ .env.local
69
+ *.pem
70
+ aws-credentials*
71
+
72
+ # OS
73
+ Thumbs.db
74
+
75
+ # SAM build context: vendored copy of api_medic (created before sam build)
76
+ deploy/lambda/api_medic/
77
+
78
+ # Cert / DNS validation scratch (generated during one-shot ACM setup,
79
+ # not used by builds or deploys)
80
+ /acm-validation.json
81
+ /cert-arn.txt
82
+
83
+ # One-shot deploy operations scratch (templates fetched from CFN,
84
+ # import maps, etc.)
85
+ /.deploy-tmp/
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ All notable changes to api-medic are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [1.0.0] - 2026-04-29
8
+
9
+ First public release. Three input surfaces (CLI, local web UI, hosted demo) sharing one core engine and a single `Report` shape.
10
+
11
+ ### Added — Core engine
12
+
13
+ - Pydantic v2 data model: `Report`, `Finding`, `RequestSummary`, `ResponseSummary`, `TimingBreakdown`, `CapturedRequest`, `CapturedResponse`, `CapturedDns`, `CapturedTls`. `schema_version` bump-tracked.
14
+ - Parser: HAR 1.2 archives and curl command strings normalized to the same `CapturedRequest` shape.
15
+ - Runner: live HTTP execution via `httpx`, with `dnspython` pre-flight DNS probe and a separate `cryptography`-based TLS cert/protocol probe. Captures DNS / TLS / TTFB / total timing, redirect chain, raw headers (preserving duplicate `Set-Cookie`).
16
+ - Check battery — 20+ diagnostic checks across:
17
+ - **Network** — `dns.no_records`, `dns.address_class`
18
+ - **TLS** — `tls.expired`, `tls.expiring_soon`, `tls.hostname_mismatch`, `tls.weak_protocol`
19
+ - **Transport** — `transport.redirect_loop`, `transport.redirect_to_http`, `transport.slow_tls`
20
+ - **Auth** — `auth.jwt_expired`, `auth.missing_authorization`, `auth.malformed_authorization`, `auth.suspicious_jwt_signature`
21
+ - **CORS** — `cors.preflight_failed`, `cors.origin_not_allowed`, `cors.credentials_misconfigured`
22
+ - **Body** — `body.malformed_json`, `body.content_length_mismatch`, `body.encoding_mismatch`
23
+ - **Rate limit** — `ratelimit.too_many_requests` (with Retry-After context)
24
+ - **Status** — `status.client_error`, `status.server_error`
25
+ - Renderers: terminal (rich), JSON, Markdown, HTML — pluggable via `--output`.
26
+
27
+ ### Added — CLI
28
+
29
+ - `api-medic <URL>` quick GET diagnosis (default subcommand).
30
+ - `api-medic run <URL>` full request with method, headers, body, auth.
31
+ - `api-medic from-curl '<command>'` parse + analyze (or just analyze) a curl command.
32
+ - `api-medic from-har <file.har>` analyze a captured HAR.
33
+ - `api-medic serve [--port 8765]` launch the local web UI.
34
+ - Output flags: `--output {terminal,json,markdown,html}`, `--save <path>`, `--no-color`, `--verbose`.
35
+
36
+ ### Added — Local web UI
37
+
38
+ - Vite + React 18 + TypeScript + Tailwind, no design system library, no state management library.
39
+ - Three tabs: Demos (fixture browser), Run (request composer hitting `/api/run`), HAR (file upload hitting `/api/analyze`).
40
+ - TypeScript types auto-generated from Pydantic JSON Schema via `make types`; CI fails if regenerated types drift from the committed file.
41
+ - React bundle ships inside the Python wheel — no separate install step for the local UI.
42
+
43
+ ### Added — Hosted demo (AWS)
44
+
45
+ - `https://api-medic.markandrewmarquez.com` on S3 + CloudFront + API Gateway HTTP API + Lambda.
46
+ - Routes: `POST /api/analyze` (captured-mode parser+engine), `POST /api/run` (live runner), `GET /api/health`.
47
+ - `/api/run` is SSRF-guarded (blocks RFC1918, link-local incl. EC2 metadata, multicast, loopback; HTTPS-only) with a 10-second per-request timeout.
48
+ - API Gateway throttling on `/api/run` (2 req/sec sustained, burst 5) plus a $10/month AWS Budget with 50%/100%/forecast email alerts.
49
+ - Lambda log group is stack-managed with 14-day retention.
50
+ - Same Report shape as the CLI and local web UI — byte-identical given the same input.
51
+ - Deploy via SAM, OIDC-based GitHub Actions role with least-privilege inline policy (committed at `deploy/iam/`).
52
+
53
+ ### Added — Demo polish
54
+
55
+ - Client-side HAR stripping in three tiers (passthrough → strip request/response bodies → first-entry only) so 12 MB browser exports survive API Gateway's 10 MB body cap. UI banner explains what was dropped.
56
+ - `VITE_DEMO_MODE=1` build path uses same-origin API base so CloudFront routes `/api/*` to the Lambda.
57
+
58
+ ### Added — Quality
59
+
60
+ - 311 Python tests + 60 frontend tests.
61
+ - CI matrix: 3 operating systems × 3 Python versions = 9 cells.
62
+ - 80% coverage gate on `core/checks`.
63
+ - Static-import audit (`tests/unit/test_lambda_imports.py`) enforces Lambda dep boundaries — no fastapi/uvicorn/typer/rich, and explicit positive assertion that the Lambda surface DOES include the runner + SSRF guard so `/api/run` can't silently regress.
64
+ - Schema-sync gate: CI regenerates `frontend/src/lib/types.ts` from Pydantic and fails if it differs from the committed file.
65
+
66
+ ### Added — Project
67
+
68
+ - 8 hand-crafted fixture reports covering distinct diagnostic scenarios; double as UI dev data, schema validation, and integration smoke tests.
69
+ - MIT license, public repo, full architecture spec at `docs/architecture.md`.
70
+
71
+ ### Architectural decisions
72
+
73
+ - **Stateless by design.** No DynamoDB, no S3 writes for user data, no CloudWatch persistence beyond default logs.
74
+ - **No FastAPI / uvicorn on Lambda.** The Lambda dispatches routes inline via `lambda_handler` to keep cold-start budget intact even with the runner deps (httpx, dnspython, cryptography).
75
+ - **Same `Report` shape from every surface** — the contract that makes the future browser extension a thin capture layer rather than a parallel implementation.
76
+
77
+ [Unreleased]: https://github.com/marky224/api-medic/compare/v1.0.0...HEAD
78
+ [1.0.0]: https://github.com/marky224/api-medic/releases/tag/v1.0.0
@@ -0,0 +1,62 @@
1
+ # CLAUDE.md — operating brief for Claude Code in this repo
2
+
3
+ This is api-medic, an open-source HTTP API troubleshooting tool. Mark Marquez directs the build; Claude implements alongside him.
4
+
5
+ ## Source of truth
6
+ - `docs/architecture.md` — the full v1 build spec. Tech stack, repo structure, data model, invariants, phases. Consult actively.
7
+ - The Pydantic models in `src/api_medic/core/models.py` are the contract every surface produces and consumes. Don''t break them without bumping `schema_version`.
8
+ - `frontend/src/lib/types.ts` is auto-generated from those models via `make types`. Never edit by hand.
9
+
10
+ ## Status
11
+ v1 is shipped. All seven phases in `docs/architecture.md` are complete:
12
+ data model, fixtures, web UI, core engine, CLI, hosted demo on AWS,
13
+ Phase 6 polish. The hosted demo at `https://api-medic.markandrewmarquez.com`
14
+ exposes Demos / Run / HAR tabs and the Lambda runs both captured-mode
15
+ and live requests (SSRF-guarded, throttled).
16
+
17
+ Default work mode is now polish, bug fixes, and post-launch items.
18
+ Phase 7 (browser extension) is the only remaining planned phase and
19
+ is explicitly post-v1.
20
+
21
+ ## Working style
22
+ - Default to producing actual code, not pseudocode.
23
+ - Write tests alongside the code.
24
+ - Push back when you disagree.
25
+ - One question at a time when scoping.
26
+ - Don''t expand scope without flagging.
27
+ - No emojis unless Mark uses them first.
28
+ - Skip sycophantic openers ("Great question!", etc.).
29
+
30
+ ## Architectural invariants (don''t change without sign-off)
31
+ - Same Report shape from every surface (CLI, web, Lambda, extension).
32
+ - Lambda surface ships parser + checks + renderers + the live runner.
33
+ Outbound HTTP from the Lambda is allowed and must apply SSRF mitigations
34
+ (block RFC1918, link-local incl. 169.254.169.254 metadata, multicast,
35
+ loopback; resolve hostname once and re-check the IP before connecting)
36
+ plus a conservative per-request timeout (≤10s). FastAPI and uvicorn
37
+ remain excluded — the Lambda dispatches routes inline via
38
+ `lambda_handler`, no web framework.
39
+ - Hosted demo exposes both captured-mode (HAR/curl) and live-run tabs.
40
+ Live runs are bounded by API Gateway throttling, Lambda reserved
41
+ concurrency, and the AWS Budget; assume any URL the user submits
42
+ will be fetched by the Lambda.
43
+ - Stateless: no DynamoDB, no S3 writes for user data, no persistence.
44
+ - Public repo, MIT license, no public timelines anywhere.
45
+
46
+ ## Tech stack (locked)
47
+ - Python 3.10+ (3.12 target), httpx, dnspython, cryptography, pydantic v2, typer, rich, fastapi, uvicorn
48
+ - TypeScript + React 18 + Vite + Tailwind. No design system library. No state management library.
49
+ - AWS SAM in us-east-1, API Gateway HTTP API → Lambda, CloudFront + S3
50
+ - GitHub Actions CI
51
+
52
+ ## Quality gates (must pass before pushing)
53
+ - `pytest` — all green
54
+ - `ruff check .` and `ruff format --check .`
55
+ - `mypy` — clean
56
+ - `make types` produces no diff vs committed `types.ts` (also verified by CI)
57
+
58
+ ## Out of scope for v1
59
+ History/share links, comparison mode, OpenAPI validation, OAuth simulation, WebSocket support, plugin system, pre-built binaries, telemetry. Don''t build these unless Mark explicitly asks.
60
+
61
+ ## Audience
62
+ README and marketing copy frame this as a tool for technical support engineers triaging customer-reported API issues. Tool itself is general-purpose.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mark Marquez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,52 @@
1
+ .PHONY: help install types lint format typecheck test test-cov clean
2
+
3
+ help: ## Show this help
4
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
5
+
6
+ install: ## Install package + dev dependencies
7
+ pip install -e ".[dev]"
8
+
9
+ types: ## Regenerate TypeScript types from Pydantic JSON Schema
10
+ @echo "Exporting JSON Schema from Pydantic models..."
11
+ @python -m api_medic.core.export_schema > /tmp/api-medic-schema.json
12
+ @echo "Generating TypeScript types via quicktype..."
13
+ @mkdir -p frontend/src/lib
14
+ @npx --yes quicktype@23 \
15
+ --src-lang schema \
16
+ --lang typescript \
17
+ --just-types \
18
+ --prefer-unions \
19
+ --no-date-times \
20
+ --top-level Report \
21
+ -o frontend/src/lib/types.ts \
22
+ /tmp/api-medic-schema.json
23
+ @echo "/* eslint-disable */" > /tmp/types.header
24
+ @echo "/**" >> /tmp/types.header
25
+ @echo " * AUTO-GENERATED by 'make types' from Pydantic models in src/api_medic/core/models.py." >> /tmp/types.header
26
+ @echo " * Do not edit by hand. Run 'make types' to regenerate." >> /tmp/types.header
27
+ @echo " */" >> /tmp/types.header
28
+ @cat /tmp/types.header frontend/src/lib/types.ts > /tmp/types.combined
29
+ @mv /tmp/types.combined frontend/src/lib/types.ts
30
+ @rm /tmp/types.header /tmp/api-medic-schema.json
31
+ @echo "Wrote frontend/src/lib/types.ts"
32
+
33
+ lint: ## Run ruff lint
34
+ ruff check .
35
+
36
+ format: ## Format code with ruff
37
+ ruff format .
38
+
39
+ typecheck: ## Run mypy
40
+ mypy src
41
+
42
+ test: ## Run tests
43
+ pytest
44
+
45
+ test-cov: ## Run tests with coverage
46
+ pytest --cov --cov-report=term-missing --cov-report=html
47
+
48
+ clean: ## Remove build artifacts and caches
49
+ rm -rf build dist *.egg-info
50
+ rm -rf .pytest_cache .mypy_cache .ruff_cache htmlcov
51
+ find . -type d -name __pycache__ -exec rm -rf {} +
52
+ find . -type f -name "*.pyc" -delete