Spaces:
Sleeping
feat: Complete GitHub App authentication, documentation generation, and test improvements
Browse files- Implement GitHub App authentication (Octokit) with fallback to personal access token
- Add @octokit/auth-app and @octokit/rest dependencies for secure GitHub API access
- Update GitHubService to use Octokit instead of axios for better API compatibility
- Create DocGeneratorAgent for auto-generating project documentation
- Add docs API endpoints for project, file, and README documentation
- Improve scanner report formatting with better issue presentation
- Update server startup logging with environment info and health check URL
- Add error handling for server startup failures
- Create comprehensive test suite (test-ai-agent.js) for CI/CD validation
- Update GitHub Actions workflow to use environment secrets
- Update installation guide with both token auth methods
- Add GitHub App setup instructions to INSTALLATION.md
- Fix environment configuration to support both auth methods
- .env.example +5 -0
- .github/workflows/sync-to-hf.yml +4 -2
- public/.env.example +8 -1
- public/INSTALLATION.md +38 -1
- public/package-lock.json +410 -1
- public/package.json +2 -0
- public/src/agents/doc-generator.js +122 -0
- public/src/agents/scanner.js +24 -9
- public/src/api/docs.js +92 -0
- public/src/config/env.js +15 -2
- public/src/server.js +13 -2
- public/src/services/github.js +85 -36
|
@@ -7,6 +7,11 @@ HF_USERNAME=NLarchive
|
|
| 7 |
HF_SPACE_NAME=my-webapp-hf
|
| 8 |
GITHUB_BRANCH=main
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
# Optional
|
| 11 |
DRY_RUN=false
|
| 12 |
FORCE_PUSH=false
|
|
|
|
| 7 |
HF_SPACE_NAME=my-webapp-hf
|
| 8 |
GITHUB_BRANCH=main
|
| 9 |
|
| 10 |
+
# GitHub App Authentication (for AI Agent)
|
| 11 |
+
GH_APP_ID=your_github_app_id
|
| 12 |
+
GH_APP_PRIVATE_KEY_B64=your_base64_encoded_private_key
|
| 13 |
+
GH_APP_INSTALLATION_ID=your_installation_id
|
| 14 |
+
|
| 15 |
# Optional
|
| 16 |
DRY_RUN=false
|
| 17 |
FORCE_PUSH=false
|
|
@@ -33,9 +33,11 @@ jobs:
|
|
| 33 |
- name: Push to Hugging Face Space
|
| 34 |
env:
|
| 35 |
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
|
|
|
|
|
|
| 36 |
run: |
|
| 37 |
# Add space remote (ignore if exists)
|
| 38 |
-
git remote add space https
|
| 39 |
|
| 40 |
# Force push to the space
|
| 41 |
-
git push --force https
|
|
|
|
| 33 |
- name: Push to Hugging Face Space
|
| 34 |
env:
|
| 35 |
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 36 |
+
HF_USERNAME: ${{ secrets.HF_USERNAME }}
|
| 37 |
+
HF_SPACE_NAME: ${{ secrets.HF_SPACE_NAME }}
|
| 38 |
run: |
|
| 39 |
# Add space remote (ignore if exists)
|
| 40 |
+
git remote add space https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$HF_SPACE_NAME || true
|
| 41 |
|
| 42 |
# Force push to the space
|
| 43 |
+
git push --force https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$HF_SPACE_NAME main
|
|
@@ -10,11 +10,18 @@
|
|
| 10 |
# Get from: https://makersuite.google.com/app/apikey
|
| 11 |
GEMINI_API_KEY=your_gemini_api_key_here
|
| 12 |
|
| 13 |
-
# GitHub
|
|
|
|
| 14 |
# Get from: https://github.com/settings/tokens
|
| 15 |
# Required scopes: repo, issues, actions
|
| 16 |
GITHUB_TOKEN=your_github_token_here
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
# Hugging Face Token
|
| 19 |
# Get from: https://huggingface.co/settings/tokens
|
| 20 |
HF_TOKEN=your_huggingface_token_here
|
|
|
|
| 10 |
# Get from: https://makersuite.google.com/app/apikey
|
| 11 |
GEMINI_API_KEY=your_gemini_api_key_here
|
| 12 |
|
| 13 |
+
# GitHub Authentication (Choose ONE method)
|
| 14 |
+
# Option 1: Personal Access Token (Simple)
|
| 15 |
# Get from: https://github.com/settings/tokens
|
| 16 |
# Required scopes: repo, issues, actions
|
| 17 |
GITHUB_TOKEN=your_github_token_here
|
| 18 |
|
| 19 |
+
# Option 2: GitHub App (Recommended for production)
|
| 20 |
+
# Get from: https://github.com/settings/apps
|
| 21 |
+
GH_APP_ID=your_github_app_id
|
| 22 |
+
GH_APP_PRIVATE_KEY_B64=your_base64_encoded_private_key
|
| 23 |
+
GH_APP_INSTALLATION_ID=your_app_installation_id
|
| 24 |
+
|
| 25 |
# Hugging Face Token
|
| 26 |
# Get from: https://huggingface.co/settings/tokens
|
| 27 |
HF_TOKEN=your_huggingface_token_here
|
|
@@ -78,7 +78,9 @@ Visit: http://localhost:3000
|
|
| 78 |
4. Paste into .env GEMINI_API_KEY=
|
| 79 |
```
|
| 80 |
|
| 81 |
-
#### 2. GitHub
|
|
|
|
|
|
|
| 82 |
```
|
| 83 |
1. Go: https://github.com/settings/tokens
|
| 84 |
2. Click "Generate new token" → "Generate new token (classic)"
|
|
@@ -91,6 +93,41 @@ Visit: http://localhost:3000
|
|
| 91 |
6. Paste into .env GITHUB_TOKEN=
|
| 92 |
```
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
#### 3. Hugging Face Token
|
| 95 |
```
|
| 96 |
1. Go: https://huggingface.co/settings/tokens
|
|
|
|
| 78 |
4. Paste into .env GEMINI_API_KEY=
|
| 79 |
```
|
| 80 |
|
| 81 |
+
#### 2. GitHub Authentication (Choose One)
|
| 82 |
+
|
| 83 |
+
**Option A: Personal Access Token (Simple)**
|
| 84 |
```
|
| 85 |
1. Go: https://github.com/settings/tokens
|
| 86 |
2. Click "Generate new token" → "Generate new token (classic)"
|
|
|
|
| 93 |
6. Paste into .env GITHUB_TOKEN=
|
| 94 |
```
|
| 95 |
|
| 96 |
+
**Option B: GitHub App (Recommended for Production)**
|
| 97 |
+
```
|
| 98 |
+
1. Go: https://github.com/settings/apps
|
| 99 |
+
2. Click "New GitHub App"
|
| 100 |
+
3. Configure:
|
| 101 |
+
- Name: "AI Agent HF"
|
| 102 |
+
- Homepage URL: https://huggingface.co/spaces/YourUsername/your-space
|
| 103 |
+
- Webhook URL: Leave empty
|
| 104 |
+
- Permissions:
|
| 105 |
+
✓ Repository permissions → Contents: Read & write
|
| 106 |
+
✓ Repository permissions → Issues: Read & write
|
| 107 |
+
✓ Repository permissions → Pull requests: Read & write
|
| 108 |
+
✓ Repository permissions → Actions: Read & write
|
| 109 |
+
✓ Repository permissions → Metadata: Read
|
| 110 |
+
4. Generate private key (download .pem file)
|
| 111 |
+
5. Install app on your repository
|
| 112 |
+
6. Get installation ID from app settings
|
| 113 |
+
7. Convert private key to base64:
|
| 114 |
+
```bash
|
| 115 |
+
# On Windows PowerShell
|
| 116 |
+
[Convert]::ToBase64String([IO.File]::ReadAllBytes("path\to\private-key.pem"))
|
| 117 |
+
```
|
| 118 |
+
8. Configure .env:
|
| 119 |
+
```
|
| 120 |
+
GH_APP_ID=your_app_id
|
| 121 |
+
GH_APP_PRIVATE_KEY_B64=your_base64_private_key
|
| 122 |
+
GH_APP_INSTALLATION_ID=your_installation_id
|
| 123 |
+
```
|
| 124 |
+
9. Test authentication:
|
| 125 |
+
```bash
|
| 126 |
+
cd scripts
|
| 127 |
+
node test-github-auth.js
|
| 128 |
+
```
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
#### 3. Hugging Face Token
|
| 132 |
```
|
| 133 |
1. Go: https://huggingface.co/settings/tokens
|
|
@@ -9,6 +9,8 @@
|
|
| 9 |
"version": "1.0.0",
|
| 10 |
"dependencies": {
|
| 11 |
"@google/generative-ai": "^0.7.0",
|
|
|
|
|
|
|
| 12 |
"axios": "^1.6.0",
|
| 13 |
"body-parser": "^1.20.2",
|
| 14 |
"cors": "^2.8.5",
|
|
@@ -30,6 +32,265 @@
|
|
| 30 |
"node": ">=18.0.0"
|
| 31 |
}
|
| 32 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
"node_modules/accepts": {
|
| 34 |
"version": "1.3.8",
|
| 35 |
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
|
@@ -81,6 +342,11 @@
|
|
| 81 |
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
| 82 |
"dev": true
|
| 83 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
"node_modules/binary-extensions": {
|
| 85 |
"version": "2.3.0",
|
| 86 |
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
|
@@ -138,6 +404,16 @@
|
|
| 138 |
"node": ">=8"
|
| 139 |
}
|
| 140 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
"node_modules/bytes": {
|
| 142 |
"version": "3.1.2",
|
| 143 |
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
|
@@ -282,6 +558,11 @@
|
|
| 282 |
"node": ">= 0.8"
|
| 283 |
}
|
| 284 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
"node_modules/destroy": {
|
| 286 |
"version": "1.2.0",
|
| 287 |
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
|
@@ -315,6 +596,14 @@
|
|
| 315 |
"node": ">= 0.4"
|
| 316 |
}
|
| 317 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
"node_modules/ee-first": {
|
| 319 |
"version": "1.1.1",
|
| 320 |
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
@@ -718,6 +1007,95 @@
|
|
| 718 |
"node": ">=0.12.0"
|
| 719 |
}
|
| 720 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 721 |
"node_modules/math-intrinsics": {
|
| 722 |
"version": "1.1.0",
|
| 723 |
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
@@ -895,6 +1273,14 @@
|
|
| 895 |
"node": ">= 0.8"
|
| 896 |
}
|
| 897 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 898 |
"node_modules/parseurl": {
|
| 899 |
"version": "1.3.3",
|
| 900 |
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
@@ -1019,7 +1405,6 @@
|
|
| 1019 |
"version": "7.7.3",
|
| 1020 |
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
| 1021 |
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
| 1022 |
-
"dev": true,
|
| 1023 |
"bin": {
|
| 1024 |
"semver": "bin/semver.js"
|
| 1025 |
},
|
|
@@ -1229,6 +1614,25 @@
|
|
| 1229 |
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
| 1230 |
"dev": true
|
| 1231 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1232 |
"node_modules/unpipe": {
|
| 1233 |
"version": "1.0.0",
|
| 1234 |
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
@@ -1252,6 +1656,11 @@
|
|
| 1252 |
"engines": {
|
| 1253 |
"node": ">= 0.8"
|
| 1254 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1255 |
}
|
| 1256 |
}
|
| 1257 |
}
|
|
|
|
| 9 |
"version": "1.0.0",
|
| 10 |
"dependencies": {
|
| 11 |
"@google/generative-ai": "^0.7.0",
|
| 12 |
+
"@octokit/auth-app": "^6.0.3",
|
| 13 |
+
"@octokit/rest": "^20.0.2",
|
| 14 |
"axios": "^1.6.0",
|
| 15 |
"body-parser": "^1.20.2",
|
| 16 |
"cors": "^2.8.5",
|
|
|
|
| 32 |
"node": ">=18.0.0"
|
| 33 |
}
|
| 34 |
},
|
| 35 |
+
"node_modules/@octokit/auth-app": {
|
| 36 |
+
"version": "6.1.4",
|
| 37 |
+
"resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz",
|
| 38 |
+
"integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==",
|
| 39 |
+
"dependencies": {
|
| 40 |
+
"@octokit/auth-oauth-app": "^7.1.0",
|
| 41 |
+
"@octokit/auth-oauth-user": "^4.1.0",
|
| 42 |
+
"@octokit/request": "^8.3.1",
|
| 43 |
+
"@octokit/request-error": "^5.1.0",
|
| 44 |
+
"@octokit/types": "^13.1.0",
|
| 45 |
+
"deprecation": "^2.3.1",
|
| 46 |
+
"lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1",
|
| 47 |
+
"universal-github-app-jwt": "^1.1.2",
|
| 48 |
+
"universal-user-agent": "^6.0.0"
|
| 49 |
+
},
|
| 50 |
+
"engines": {
|
| 51 |
+
"node": ">= 18"
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"node_modules/@octokit/auth-oauth-app": {
|
| 55 |
+
"version": "7.1.0",
|
| 56 |
+
"resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz",
|
| 57 |
+
"integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==",
|
| 58 |
+
"dependencies": {
|
| 59 |
+
"@octokit/auth-oauth-device": "^6.1.0",
|
| 60 |
+
"@octokit/auth-oauth-user": "^4.1.0",
|
| 61 |
+
"@octokit/request": "^8.3.1",
|
| 62 |
+
"@octokit/types": "^13.0.0",
|
| 63 |
+
"@types/btoa-lite": "^1.0.0",
|
| 64 |
+
"btoa-lite": "^1.0.0",
|
| 65 |
+
"universal-user-agent": "^6.0.0"
|
| 66 |
+
},
|
| 67 |
+
"engines": {
|
| 68 |
+
"node": ">= 18"
|
| 69 |
+
}
|
| 70 |
+
},
|
| 71 |
+
"node_modules/@octokit/auth-oauth-device": {
|
| 72 |
+
"version": "6.1.0",
|
| 73 |
+
"resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz",
|
| 74 |
+
"integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==",
|
| 75 |
+
"dependencies": {
|
| 76 |
+
"@octokit/oauth-methods": "^4.1.0",
|
| 77 |
+
"@octokit/request": "^8.3.1",
|
| 78 |
+
"@octokit/types": "^13.0.0",
|
| 79 |
+
"universal-user-agent": "^6.0.0"
|
| 80 |
+
},
|
| 81 |
+
"engines": {
|
| 82 |
+
"node": ">= 18"
|
| 83 |
+
}
|
| 84 |
+
},
|
| 85 |
+
"node_modules/@octokit/auth-oauth-user": {
|
| 86 |
+
"version": "4.1.0",
|
| 87 |
+
"resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz",
|
| 88 |
+
"integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==",
|
| 89 |
+
"dependencies": {
|
| 90 |
+
"@octokit/auth-oauth-device": "^6.1.0",
|
| 91 |
+
"@octokit/oauth-methods": "^4.1.0",
|
| 92 |
+
"@octokit/request": "^8.3.1",
|
| 93 |
+
"@octokit/types": "^13.0.0",
|
| 94 |
+
"btoa-lite": "^1.0.0",
|
| 95 |
+
"universal-user-agent": "^6.0.0"
|
| 96 |
+
},
|
| 97 |
+
"engines": {
|
| 98 |
+
"node": ">= 18"
|
| 99 |
+
}
|
| 100 |
+
},
|
| 101 |
+
"node_modules/@octokit/auth-token": {
|
| 102 |
+
"version": "4.0.0",
|
| 103 |
+
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
|
| 104 |
+
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
|
| 105 |
+
"engines": {
|
| 106 |
+
"node": ">= 18"
|
| 107 |
+
}
|
| 108 |
+
},
|
| 109 |
+
"node_modules/@octokit/core": {
|
| 110 |
+
"version": "5.2.2",
|
| 111 |
+
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
|
| 112 |
+
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
|
| 113 |
+
"dependencies": {
|
| 114 |
+
"@octokit/auth-token": "^4.0.0",
|
| 115 |
+
"@octokit/graphql": "^7.1.0",
|
| 116 |
+
"@octokit/request": "^8.4.1",
|
| 117 |
+
"@octokit/request-error": "^5.1.1",
|
| 118 |
+
"@octokit/types": "^13.0.0",
|
| 119 |
+
"before-after-hook": "^2.2.0",
|
| 120 |
+
"universal-user-agent": "^6.0.0"
|
| 121 |
+
},
|
| 122 |
+
"engines": {
|
| 123 |
+
"node": ">= 18"
|
| 124 |
+
}
|
| 125 |
+
},
|
| 126 |
+
"node_modules/@octokit/endpoint": {
|
| 127 |
+
"version": "9.0.6",
|
| 128 |
+
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
|
| 129 |
+
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
|
| 130 |
+
"dependencies": {
|
| 131 |
+
"@octokit/types": "^13.1.0",
|
| 132 |
+
"universal-user-agent": "^6.0.0"
|
| 133 |
+
},
|
| 134 |
+
"engines": {
|
| 135 |
+
"node": ">= 18"
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
"node_modules/@octokit/graphql": {
|
| 139 |
+
"version": "7.1.1",
|
| 140 |
+
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
|
| 141 |
+
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
|
| 142 |
+
"dependencies": {
|
| 143 |
+
"@octokit/request": "^8.4.1",
|
| 144 |
+
"@octokit/types": "^13.0.0",
|
| 145 |
+
"universal-user-agent": "^6.0.0"
|
| 146 |
+
},
|
| 147 |
+
"engines": {
|
| 148 |
+
"node": ">= 18"
|
| 149 |
+
}
|
| 150 |
+
},
|
| 151 |
+
"node_modules/@octokit/oauth-authorization-url": {
|
| 152 |
+
"version": "6.0.2",
|
| 153 |
+
"resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz",
|
| 154 |
+
"integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==",
|
| 155 |
+
"engines": {
|
| 156 |
+
"node": ">= 18"
|
| 157 |
+
}
|
| 158 |
+
},
|
| 159 |
+
"node_modules/@octokit/oauth-methods": {
|
| 160 |
+
"version": "4.1.0",
|
| 161 |
+
"resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz",
|
| 162 |
+
"integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==",
|
| 163 |
+
"dependencies": {
|
| 164 |
+
"@octokit/oauth-authorization-url": "^6.0.2",
|
| 165 |
+
"@octokit/request": "^8.3.1",
|
| 166 |
+
"@octokit/request-error": "^5.1.0",
|
| 167 |
+
"@octokit/types": "^13.0.0",
|
| 168 |
+
"btoa-lite": "^1.0.0"
|
| 169 |
+
},
|
| 170 |
+
"engines": {
|
| 171 |
+
"node": ">= 18"
|
| 172 |
+
}
|
| 173 |
+
},
|
| 174 |
+
"node_modules/@octokit/openapi-types": {
|
| 175 |
+
"version": "24.2.0",
|
| 176 |
+
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
| 177 |
+
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="
|
| 178 |
+
},
|
| 179 |
+
"node_modules/@octokit/plugin-paginate-rest": {
|
| 180 |
+
"version": "11.4.4-cjs.2",
|
| 181 |
+
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz",
|
| 182 |
+
"integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==",
|
| 183 |
+
"dependencies": {
|
| 184 |
+
"@octokit/types": "^13.7.0"
|
| 185 |
+
},
|
| 186 |
+
"engines": {
|
| 187 |
+
"node": ">= 18"
|
| 188 |
+
},
|
| 189 |
+
"peerDependencies": {
|
| 190 |
+
"@octokit/core": "5"
|
| 191 |
+
}
|
| 192 |
+
},
|
| 193 |
+
"node_modules/@octokit/plugin-request-log": {
|
| 194 |
+
"version": "4.0.1",
|
| 195 |
+
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
|
| 196 |
+
"integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==",
|
| 197 |
+
"engines": {
|
| 198 |
+
"node": ">= 18"
|
| 199 |
+
},
|
| 200 |
+
"peerDependencies": {
|
| 201 |
+
"@octokit/core": "5"
|
| 202 |
+
}
|
| 203 |
+
},
|
| 204 |
+
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
| 205 |
+
"version": "13.3.2-cjs.1",
|
| 206 |
+
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz",
|
| 207 |
+
"integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==",
|
| 208 |
+
"dependencies": {
|
| 209 |
+
"@octokit/types": "^13.8.0"
|
| 210 |
+
},
|
| 211 |
+
"engines": {
|
| 212 |
+
"node": ">= 18"
|
| 213 |
+
},
|
| 214 |
+
"peerDependencies": {
|
| 215 |
+
"@octokit/core": "^5"
|
| 216 |
+
}
|
| 217 |
+
},
|
| 218 |
+
"node_modules/@octokit/request": {
|
| 219 |
+
"version": "8.4.1",
|
| 220 |
+
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
|
| 221 |
+
"integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
|
| 222 |
+
"dependencies": {
|
| 223 |
+
"@octokit/endpoint": "^9.0.6",
|
| 224 |
+
"@octokit/request-error": "^5.1.1",
|
| 225 |
+
"@octokit/types": "^13.1.0",
|
| 226 |
+
"universal-user-agent": "^6.0.0"
|
| 227 |
+
},
|
| 228 |
+
"engines": {
|
| 229 |
+
"node": ">= 18"
|
| 230 |
+
}
|
| 231 |
+
},
|
| 232 |
+
"node_modules/@octokit/request-error": {
|
| 233 |
+
"version": "5.1.1",
|
| 234 |
+
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
|
| 235 |
+
"integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
|
| 236 |
+
"dependencies": {
|
| 237 |
+
"@octokit/types": "^13.1.0",
|
| 238 |
+
"deprecation": "^2.0.0",
|
| 239 |
+
"once": "^1.4.0"
|
| 240 |
+
},
|
| 241 |
+
"engines": {
|
| 242 |
+
"node": ">= 18"
|
| 243 |
+
}
|
| 244 |
+
},
|
| 245 |
+
"node_modules/@octokit/rest": {
|
| 246 |
+
"version": "20.1.2",
|
| 247 |
+
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz",
|
| 248 |
+
"integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==",
|
| 249 |
+
"dependencies": {
|
| 250 |
+
"@octokit/core": "^5.0.2",
|
| 251 |
+
"@octokit/plugin-paginate-rest": "11.4.4-cjs.2",
|
| 252 |
+
"@octokit/plugin-request-log": "^4.0.0",
|
| 253 |
+
"@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1"
|
| 254 |
+
},
|
| 255 |
+
"engines": {
|
| 256 |
+
"node": ">= 18"
|
| 257 |
+
}
|
| 258 |
+
},
|
| 259 |
+
"node_modules/@octokit/types": {
|
| 260 |
+
"version": "13.10.0",
|
| 261 |
+
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
| 262 |
+
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
| 263 |
+
"dependencies": {
|
| 264 |
+
"@octokit/openapi-types": "^24.2.0"
|
| 265 |
+
}
|
| 266 |
+
},
|
| 267 |
+
"node_modules/@types/btoa-lite": {
|
| 268 |
+
"version": "1.0.2",
|
| 269 |
+
"resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz",
|
| 270 |
+
"integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="
|
| 271 |
+
},
|
| 272 |
+
"node_modules/@types/jsonwebtoken": {
|
| 273 |
+
"version": "9.0.10",
|
| 274 |
+
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
| 275 |
+
"integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
|
| 276 |
+
"dependencies": {
|
| 277 |
+
"@types/ms": "*",
|
| 278 |
+
"@types/node": "*"
|
| 279 |
+
}
|
| 280 |
+
},
|
| 281 |
+
"node_modules/@types/ms": {
|
| 282 |
+
"version": "2.1.0",
|
| 283 |
+
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
| 284 |
+
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
|
| 285 |
+
},
|
| 286 |
+
"node_modules/@types/node": {
|
| 287 |
+
"version": "24.10.1",
|
| 288 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
| 289 |
+
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
| 290 |
+
"dependencies": {
|
| 291 |
+
"undici-types": "~7.16.0"
|
| 292 |
+
}
|
| 293 |
+
},
|
| 294 |
"node_modules/accepts": {
|
| 295 |
"version": "1.3.8",
|
| 296 |
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
|
|
|
| 342 |
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
| 343 |
"dev": true
|
| 344 |
},
|
| 345 |
+
"node_modules/before-after-hook": {
|
| 346 |
+
"version": "2.2.3",
|
| 347 |
+
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
| 348 |
+
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
| 349 |
+
},
|
| 350 |
"node_modules/binary-extensions": {
|
| 351 |
"version": "2.3.0",
|
| 352 |
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
|
|
|
| 404 |
"node": ">=8"
|
| 405 |
}
|
| 406 |
},
|
| 407 |
+
"node_modules/btoa-lite": {
|
| 408 |
+
"version": "1.0.0",
|
| 409 |
+
"resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
|
| 410 |
+
"integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA=="
|
| 411 |
+
},
|
| 412 |
+
"node_modules/buffer-equal-constant-time": {
|
| 413 |
+
"version": "1.0.1",
|
| 414 |
+
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
| 415 |
+
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
| 416 |
+
},
|
| 417 |
"node_modules/bytes": {
|
| 418 |
"version": "3.1.2",
|
| 419 |
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
|
|
|
| 558 |
"node": ">= 0.8"
|
| 559 |
}
|
| 560 |
},
|
| 561 |
+
"node_modules/deprecation": {
|
| 562 |
+
"version": "2.3.1",
|
| 563 |
+
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
| 564 |
+
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
| 565 |
+
},
|
| 566 |
"node_modules/destroy": {
|
| 567 |
"version": "1.2.0",
|
| 568 |
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
|
|
|
| 596 |
"node": ">= 0.4"
|
| 597 |
}
|
| 598 |
},
|
| 599 |
+
"node_modules/ecdsa-sig-formatter": {
|
| 600 |
+
"version": "1.0.11",
|
| 601 |
+
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
| 602 |
+
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
| 603 |
+
"dependencies": {
|
| 604 |
+
"safe-buffer": "^5.0.1"
|
| 605 |
+
}
|
| 606 |
+
},
|
| 607 |
"node_modules/ee-first": {
|
| 608 |
"version": "1.1.1",
|
| 609 |
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
|
|
| 1007 |
"node": ">=0.12.0"
|
| 1008 |
}
|
| 1009 |
},
|
| 1010 |
+
"node_modules/jsonwebtoken": {
|
| 1011 |
+
"version": "9.0.2",
|
| 1012 |
+
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
| 1013 |
+
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
| 1014 |
+
"dependencies": {
|
| 1015 |
+
"jws": "^3.2.2",
|
| 1016 |
+
"lodash.includes": "^4.3.0",
|
| 1017 |
+
"lodash.isboolean": "^3.0.3",
|
| 1018 |
+
"lodash.isinteger": "^4.0.4",
|
| 1019 |
+
"lodash.isnumber": "^3.0.3",
|
| 1020 |
+
"lodash.isplainobject": "^4.0.6",
|
| 1021 |
+
"lodash.isstring": "^4.0.1",
|
| 1022 |
+
"lodash.once": "^4.0.0",
|
| 1023 |
+
"ms": "^2.1.1",
|
| 1024 |
+
"semver": "^7.5.4"
|
| 1025 |
+
},
|
| 1026 |
+
"engines": {
|
| 1027 |
+
"node": ">=12",
|
| 1028 |
+
"npm": ">=6"
|
| 1029 |
+
}
|
| 1030 |
+
},
|
| 1031 |
+
"node_modules/jsonwebtoken/node_modules/ms": {
|
| 1032 |
+
"version": "2.1.3",
|
| 1033 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 1034 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
| 1035 |
+
},
|
| 1036 |
+
"node_modules/jwa": {
|
| 1037 |
+
"version": "1.4.2",
|
| 1038 |
+
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
|
| 1039 |
+
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
|
| 1040 |
+
"dependencies": {
|
| 1041 |
+
"buffer-equal-constant-time": "^1.0.1",
|
| 1042 |
+
"ecdsa-sig-formatter": "1.0.11",
|
| 1043 |
+
"safe-buffer": "^5.0.1"
|
| 1044 |
+
}
|
| 1045 |
+
},
|
| 1046 |
+
"node_modules/jws": {
|
| 1047 |
+
"version": "3.2.2",
|
| 1048 |
+
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
| 1049 |
+
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
| 1050 |
+
"dependencies": {
|
| 1051 |
+
"jwa": "^1.4.1",
|
| 1052 |
+
"safe-buffer": "^5.0.1"
|
| 1053 |
+
}
|
| 1054 |
+
},
|
| 1055 |
+
"node_modules/lodash.includes": {
|
| 1056 |
+
"version": "4.3.0",
|
| 1057 |
+
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
| 1058 |
+
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
| 1059 |
+
},
|
| 1060 |
+
"node_modules/lodash.isboolean": {
|
| 1061 |
+
"version": "3.0.3",
|
| 1062 |
+
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
| 1063 |
+
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
| 1064 |
+
},
|
| 1065 |
+
"node_modules/lodash.isinteger": {
|
| 1066 |
+
"version": "4.0.4",
|
| 1067 |
+
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
| 1068 |
+
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
| 1069 |
+
},
|
| 1070 |
+
"node_modules/lodash.isnumber": {
|
| 1071 |
+
"version": "3.0.3",
|
| 1072 |
+
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
| 1073 |
+
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
| 1074 |
+
},
|
| 1075 |
+
"node_modules/lodash.isplainobject": {
|
| 1076 |
+
"version": "4.0.6",
|
| 1077 |
+
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
| 1078 |
+
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
| 1079 |
+
},
|
| 1080 |
+
"node_modules/lodash.isstring": {
|
| 1081 |
+
"version": "4.0.1",
|
| 1082 |
+
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
| 1083 |
+
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
| 1084 |
+
},
|
| 1085 |
+
"node_modules/lodash.once": {
|
| 1086 |
+
"version": "4.1.1",
|
| 1087 |
+
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
| 1088 |
+
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
| 1089 |
+
},
|
| 1090 |
+
"node_modules/lru-cache": {
|
| 1091 |
+
"name": "@wolfy1339/lru-cache",
|
| 1092 |
+
"version": "11.0.2-patch.1",
|
| 1093 |
+
"resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz",
|
| 1094 |
+
"integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==",
|
| 1095 |
+
"engines": {
|
| 1096 |
+
"node": "18 >=18.20 || 20 || >=22"
|
| 1097 |
+
}
|
| 1098 |
+
},
|
| 1099 |
"node_modules/math-intrinsics": {
|
| 1100 |
"version": "1.1.0",
|
| 1101 |
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
|
|
| 1273 |
"node": ">= 0.8"
|
| 1274 |
}
|
| 1275 |
},
|
| 1276 |
+
"node_modules/once": {
|
| 1277 |
+
"version": "1.4.0",
|
| 1278 |
+
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
| 1279 |
+
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
| 1280 |
+
"dependencies": {
|
| 1281 |
+
"wrappy": "1"
|
| 1282 |
+
}
|
| 1283 |
+
},
|
| 1284 |
"node_modules/parseurl": {
|
| 1285 |
"version": "1.3.3",
|
| 1286 |
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
|
|
| 1405 |
"version": "7.7.3",
|
| 1406 |
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
| 1407 |
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
|
|
|
| 1408 |
"bin": {
|
| 1409 |
"semver": "bin/semver.js"
|
| 1410 |
},
|
|
|
|
| 1614 |
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
| 1615 |
"dev": true
|
| 1616 |
},
|
| 1617 |
+
"node_modules/undici-types": {
|
| 1618 |
+
"version": "7.16.0",
|
| 1619 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
| 1620 |
+
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="
|
| 1621 |
+
},
|
| 1622 |
+
"node_modules/universal-github-app-jwt": {
|
| 1623 |
+
"version": "1.2.0",
|
| 1624 |
+
"resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz",
|
| 1625 |
+
"integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==",
|
| 1626 |
+
"dependencies": {
|
| 1627 |
+
"@types/jsonwebtoken": "^9.0.0",
|
| 1628 |
+
"jsonwebtoken": "^9.0.2"
|
| 1629 |
+
}
|
| 1630 |
+
},
|
| 1631 |
+
"node_modules/universal-user-agent": {
|
| 1632 |
+
"version": "6.0.1",
|
| 1633 |
+
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
|
| 1634 |
+
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
|
| 1635 |
+
},
|
| 1636 |
"node_modules/unpipe": {
|
| 1637 |
"version": "1.0.0",
|
| 1638 |
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
|
|
| 1656 |
"engines": {
|
| 1657 |
"node": ">= 0.8"
|
| 1658 |
}
|
| 1659 |
+
},
|
| 1660 |
+
"node_modules/wrappy": {
|
| 1661 |
+
"version": "1.0.2",
|
| 1662 |
+
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
| 1663 |
+
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
| 1664 |
}
|
| 1665 |
}
|
| 1666 |
}
|
|
@@ -12,6 +12,8 @@
|
|
| 12 |
},
|
| 13 |
"dependencies": {
|
| 14 |
"@google/generative-ai": "^0.7.0",
|
|
|
|
|
|
|
| 15 |
"express": "^4.18.2",
|
| 16 |
"axios": "^1.6.0",
|
| 17 |
"dotenv": "^16.3.1",
|
|
|
|
| 12 |
},
|
| 13 |
"dependencies": {
|
| 14 |
"@google/generative-ai": "^0.7.0",
|
| 15 |
+
"@octokit/auth-app": "^6.0.3",
|
| 16 |
+
"@octokit/rest": "^20.0.2",
|
| 17 |
"express": "^4.18.2",
|
| 18 |
"axios": "^1.6.0",
|
| 19 |
"dotenv": "^16.3.1",
|
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Documentation Generator Agent
|
| 3 |
+
* Auto-generates documentation for project files
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import { projectService } from '../services/project.js';
|
| 7 |
+
import { geminiService } from '../services/gemini.js';
|
| 8 |
+
import { logger } from '../config/logger.js';
|
| 9 |
+
|
| 10 |
+
class DocGeneratorAgent {
|
| 11 |
+
/**
|
| 12 |
+
* Generate documentation for project structure
|
| 13 |
+
* @param {Object} structure - Project structure
|
| 14 |
+
* @returns {Promise<string>} Generated documentation
|
| 15 |
+
*/
|
| 16 |
+
async generateProjectDocumentation(structure) {
|
| 17 |
+
try {
|
| 18 |
+
const prompt = `Generate concise documentation for this project structure. Include:
|
| 19 |
+
1. Project overview
|
| 20 |
+
2. Directory structure explanation
|
| 21 |
+
3. Key files purpose
|
| 22 |
+
4. How to get started
|
| 23 |
+
|
| 24 |
+
Project structure:
|
| 25 |
+
- Files: ${structure.files.map((f) => f.name).join(', ')}
|
| 26 |
+
- Directories: ${structure.directories.map((d) => d.name).join(', ')}`;
|
| 27 |
+
|
| 28 |
+
const doc = await geminiService.generate(prompt);
|
| 29 |
+
return doc;
|
| 30 |
+
} catch (error) {
|
| 31 |
+
logger.error('Failed to generate documentation', { error: error.message });
|
| 32 |
+
throw error;
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* Generate file-specific documentation
|
| 38 |
+
* @param {string} filePath - Path to file
|
| 39 |
+
* @returns {Promise<string>} File documentation
|
| 40 |
+
*/
|
| 41 |
+
async generateFileDocumentation(filePath) {
|
| 42 |
+
try {
|
| 43 |
+
const content = await projectService.getFileContent(filePath);
|
| 44 |
+
const language = this.getLanguageFromPath(filePath);
|
| 45 |
+
|
| 46 |
+
const prompt = `Analyze this ${language} file and generate:
|
| 47 |
+
1. Purpose/Summary
|
| 48 |
+
2. Key functions or exports
|
| 49 |
+
3. Dependencies
|
| 50 |
+
4. Usage example (if applicable)
|
| 51 |
+
|
| 52 |
+
Code:
|
| 53 |
+
\`\`\`${language}
|
| 54 |
+
${content.substring(0, 2000)}
|
| 55 |
+
\`\`\``;
|
| 56 |
+
|
| 57 |
+
const doc = await geminiService.generate(prompt);
|
| 58 |
+
return doc;
|
| 59 |
+
} catch (error) {
|
| 60 |
+
logger.error('Failed to generate file documentation', {
|
| 61 |
+
error: error.message,
|
| 62 |
+
file: filePath,
|
| 63 |
+
});
|
| 64 |
+
throw error;
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* Generate README for project
|
| 70 |
+
* @returns {Promise<string>} Generated README
|
| 71 |
+
*/
|
| 72 |
+
async generateReadme() {
|
| 73 |
+
try {
|
| 74 |
+
const structure = await projectService.getProjectStructure();
|
| 75 |
+
const keyFiles = structure.files.filter(
|
| 76 |
+
(f) =>
|
| 77 |
+
f.name.match(/package\.json|\.env\.example|Dockerfile|docker-compose/i) ||
|
| 78 |
+
f.name.endsWith('.md')
|
| 79 |
+
);
|
| 80 |
+
|
| 81 |
+
const prompt = `Create a professional README.md for a Node.js AI agent project. Include:
|
| 82 |
+
1. Project title and description
|
| 83 |
+
2. Features
|
| 84 |
+
3. Prerequisites
|
| 85 |
+
4. Installation steps
|
| 86 |
+
5. Configuration (with example .env)
|
| 87 |
+
6. Running the application
|
| 88 |
+
7. API documentation
|
| 89 |
+
8. Architecture overview
|
| 90 |
+
9. Contributing
|
| 91 |
+
10. License
|
| 92 |
+
|
| 93 |
+
Key files in project: ${keyFiles.map((f) => f.name).join(', ')}`;
|
| 94 |
+
|
| 95 |
+
const readme = await geminiService.generate(prompt);
|
| 96 |
+
return readme;
|
| 97 |
+
} catch (error) {
|
| 98 |
+
logger.error('Failed to generate README', { error: error.message });
|
| 99 |
+
throw error;
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
/**
|
| 104 |
+
* Helper: Get language from file path
|
| 105 |
+
* @param {string} filePath - File path
|
| 106 |
+
* @returns {string} Language
|
| 107 |
+
*/
|
| 108 |
+
getLanguageFromPath(filePath) {
|
| 109 |
+
if (filePath.endsWith('.js')) return 'javascript';
|
| 110 |
+
if (filePath.endsWith('.ts')) return 'typescript';
|
| 111 |
+
if (filePath.endsWith('.jsx')) return 'jsx';
|
| 112 |
+
if (filePath.endsWith('.tsx')) return 'tsx';
|
| 113 |
+
if (filePath.endsWith('.py')) return 'python';
|
| 114 |
+
if (filePath.endsWith('.java')) return 'java';
|
| 115 |
+
if (filePath.endsWith('.go')) return 'go';
|
| 116 |
+
if (filePath.endsWith('.json')) return 'json';
|
| 117 |
+
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) return 'yaml';
|
| 118 |
+
return 'text';
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
export const docGeneratorAgent = new DocGeneratorAgent();
|
|
@@ -188,14 +188,14 @@ For each suggestion provide:
|
|
| 188 |
const issueBody = this.formatScanReportAsIssue(report);
|
| 189 |
|
| 190 |
const issue = await githubService.createIssue({
|
| 191 |
-
title: `🤖 AI Scanner: Issues Detected`,
|
| 192 |
body: issueBody,
|
| 193 |
-
labels: ['ai-detected', 'needs-review'],
|
| 194 |
});
|
| 195 |
|
| 196 |
-
logger.info('Created issue from scan', { issueNumber: issue.number });
|
| 197 |
} catch (error) {
|
| 198 |
-
logger.error('Failed to create issue', { error: error.message });
|
| 199 |
}
|
| 200 |
}
|
| 201 |
|
|
@@ -206,24 +206,39 @@ For each suggestion provide:
|
|
| 206 |
*/
|
| 207 |
formatScanReportAsIssue(report) {
|
| 208 |
let body = '## 🤖 AI Scanner Report\n\n';
|
|
|
|
|
|
|
| 209 |
|
| 210 |
if (report.issues.length > 0) {
|
| 211 |
-
body += '### Issues Found\n';
|
| 212 |
report.issues.forEach((issue) => {
|
| 213 |
body += `- **[${issue.severity}]** ${issue.message}\n`;
|
| 214 |
body += ` - File: \`${issue.file}\`\n`;
|
| 215 |
body += ` - Type: ${issue.type}\n\n`;
|
| 216 |
});
|
|
|
|
|
|
|
| 217 |
}
|
| 218 |
|
| 219 |
-
if (report.recommendations.length > 0) {
|
| 220 |
-
body += '### Recommendations\n';
|
| 221 |
report.recommendations.forEach((rec) => {
|
| 222 |
-
body += `- **[${rec.priority}]** ${rec.suggestion}\n`;
|
| 223 |
});
|
| 224 |
}
|
| 225 |
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
return body;
|
| 229 |
}
|
|
|
|
| 188 |
const issueBody = this.formatScanReportAsIssue(report);
|
| 189 |
|
| 190 |
const issue = await githubService.createIssue({
|
| 191 |
+
title: `🤖 AI Scanner: Issues Detected (${new Date().toLocaleDateString()})`,
|
| 192 |
body: issueBody,
|
| 193 |
+
labels: ['ai-detected', 'needs-review', 'auto-generated'],
|
| 194 |
});
|
| 195 |
|
| 196 |
+
logger.info('Created issue from scan', { issueNumber: issue.number, repo: config.GITHUB_REPO });
|
| 197 |
} catch (error) {
|
| 198 |
+
logger.error('Failed to create issue', { error: error.message, repo: config.GITHUB_REPO });
|
| 199 |
}
|
| 200 |
}
|
| 201 |
|
|
|
|
| 206 |
*/
|
| 207 |
formatScanReportAsIssue(report) {
|
| 208 |
let body = '## 🤖 AI Scanner Report\n\n';
|
| 209 |
+
body += `**Repository:** ${config.GITHUB_REPO}\n`;
|
| 210 |
+
body += `**Branch:** ${config.GITHUB_BRANCH}\n\n`;
|
| 211 |
|
| 212 |
if (report.issues.length > 0) {
|
| 213 |
+
body += '### 🚨 Issues Found\n';
|
| 214 |
report.issues.forEach((issue) => {
|
| 215 |
body += `- **[${issue.severity}]** ${issue.message}\n`;
|
| 216 |
body += ` - File: \`${issue.file}\`\n`;
|
| 217 |
body += ` - Type: ${issue.type}\n\n`;
|
| 218 |
});
|
| 219 |
+
} else {
|
| 220 |
+
body += '### ✅ No Critical Issues Found\n\n';
|
| 221 |
}
|
| 222 |
|
| 223 |
+
if (report.recommendations && report.recommendations.length > 0) {
|
| 224 |
+
body += '### 💡 Recommendations\n';
|
| 225 |
report.recommendations.forEach((rec) => {
|
| 226 |
+
body += `- **[${rec.priority}]** ${rec.suggestion}\n\n`;
|
| 227 |
});
|
| 228 |
}
|
| 229 |
|
| 230 |
+
if (report.analysis && report.analysis.files.length > 0) {
|
| 231 |
+
body += '### 📊 Code Analysis\n';
|
| 232 |
+
report.analysis.files.forEach((f) => {
|
| 233 |
+
body += `- **${f.name}**: Analyzed successfully\n`;
|
| 234 |
+
});
|
| 235 |
+
body += '\n';
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
body += `---\n`;
|
| 239 |
+
body += `_Scanned at: ${report.timestamp}_\n`;
|
| 240 |
+
body += `_Scan duration: ${report.duration}ms_\n`;
|
| 241 |
+
body += `_By: AI Scanner Agent_`;
|
| 242 |
|
| 243 |
return body;
|
| 244 |
}
|
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Documentation API Routes
|
| 3 |
+
* Endpoints for generating project documentation
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
import express from 'express';
|
| 7 |
+
import { docGeneratorAgent } from '../agents/doc-generator.js';
|
| 8 |
+
import { projectService } from '../services/project.js';
|
| 9 |
+
import { logger } from '../config/logger.js';
|
| 10 |
+
|
| 11 |
+
const router = express.Router();
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* POST /api/docs/project
|
| 15 |
+
* Generate project documentation
|
| 16 |
+
*/
|
| 17 |
+
router.post('/project', async (req, res) => {
|
| 18 |
+
try {
|
| 19 |
+
logger.info('Generating project documentation');
|
| 20 |
+
const structure = await projectService.getProjectStructure();
|
| 21 |
+
const doc = await docGeneratorAgent.generateProjectDocumentation(structure);
|
| 22 |
+
|
| 23 |
+
res.json({
|
| 24 |
+
success: true,
|
| 25 |
+
documentation: doc,
|
| 26 |
+
timestamp: new Date().toISOString(),
|
| 27 |
+
});
|
| 28 |
+
} catch (error) {
|
| 29 |
+
logger.error('Failed to generate project documentation', { error: error.message });
|
| 30 |
+
res.status(500).json({
|
| 31 |
+
error: 'Documentation generation failed',
|
| 32 |
+
details: error.message,
|
| 33 |
+
});
|
| 34 |
+
}
|
| 35 |
+
});
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* POST /api/docs/file
|
| 39 |
+
* Generate documentation for a specific file
|
| 40 |
+
*/
|
| 41 |
+
router.post('/file', async (req, res) => {
|
| 42 |
+
try {
|
| 43 |
+
const { filePath } = req.body;
|
| 44 |
+
|
| 45 |
+
if (!filePath) {
|
| 46 |
+
return res.status(400).json({
|
| 47 |
+
error: 'Missing required field: filePath',
|
| 48 |
+
});
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
logger.info('Generating file documentation', { file: filePath });
|
| 52 |
+
const doc = await docGeneratorAgent.generateFileDocumentation(filePath);
|
| 53 |
+
|
| 54 |
+
res.json({
|
| 55 |
+
success: true,
|
| 56 |
+
file: filePath,
|
| 57 |
+
documentation: doc,
|
| 58 |
+
timestamp: new Date().toISOString(),
|
| 59 |
+
});
|
| 60 |
+
} catch (error) {
|
| 61 |
+
logger.error('Failed to generate file documentation', { error: error.message });
|
| 62 |
+
res.status(500).json({
|
| 63 |
+
error: 'File documentation generation failed',
|
| 64 |
+
details: error.message,
|
| 65 |
+
});
|
| 66 |
+
}
|
| 67 |
+
});
|
| 68 |
+
|
| 69 |
+
/**
|
| 70 |
+
* POST /api/docs/readme
|
| 71 |
+
* Generate README for project
|
| 72 |
+
*/
|
| 73 |
+
router.post('/readme', async (req, res) => {
|
| 74 |
+
try {
|
| 75 |
+
logger.info('Generating README');
|
| 76 |
+
const readme = await docGeneratorAgent.generateReadme();
|
| 77 |
+
|
| 78 |
+
res.json({
|
| 79 |
+
success: true,
|
| 80 |
+
readme,
|
| 81 |
+
timestamp: new Date().toISOString(),
|
| 82 |
+
});
|
| 83 |
+
} catch (error) {
|
| 84 |
+
logger.error('Failed to generate README', { error: error.message });
|
| 85 |
+
res.status(500).json({
|
| 86 |
+
error: 'README generation failed',
|
| 87 |
+
details: error.message,
|
| 88 |
+
});
|
| 89 |
+
}
|
| 90 |
+
});
|
| 91 |
+
|
| 92 |
+
export default router;
|
|
@@ -10,9 +10,14 @@ export const config = {
|
|
| 10 |
|
| 11 |
// API Keys
|
| 12 |
GEMINI_API_KEY: process.env.GEMINI_API_KEY || '',
|
| 13 |
-
GITHUB_TOKEN: process.env.GITHUB_TOKEN || '',
|
| 14 |
HF_TOKEN: process.env.HF_TOKEN || '',
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
// GitHub
|
| 17 |
GITHUB_REPO: process.env.GITHUB_REPO || 'NLarchive/my-webapp-hf',
|
| 18 |
GITHUB_OWNER: process.env.GITHUB_OWNER || 'NLarchive',
|
|
@@ -33,9 +38,17 @@ export const config = {
|
|
| 33 |
};
|
| 34 |
|
| 35 |
export function validateConfig() {
|
| 36 |
-
const required = ['GEMINI_API_KEY'
|
|
|
|
|
|
|
| 37 |
const missing = required.filter(key => !config[key]);
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
if (missing.length > 0) {
|
| 40 |
console.error(`Missing required environment variables: ${missing.join(', ')}`);
|
| 41 |
process.exit(1);
|
|
|
|
| 10 |
|
| 11 |
// API Keys
|
| 12 |
GEMINI_API_KEY: process.env.GEMINI_API_KEY || '',
|
| 13 |
+
GITHUB_TOKEN: process.env.GITHUB_TOKEN || '', // Fallback for backward compatibility
|
| 14 |
HF_TOKEN: process.env.HF_TOKEN || '',
|
| 15 |
|
| 16 |
+
// GitHub App Authentication
|
| 17 |
+
GH_APP_ID: process.env.GH_APP_ID || '',
|
| 18 |
+
GH_APP_PRIVATE_KEY_B64: process.env.GH_APP_PRIVATE_KEY_B64 || '',
|
| 19 |
+
GH_APP_INSTALLATION_ID: process.env.GH_APP_INSTALLATION_ID || '',
|
| 20 |
+
|
| 21 |
// GitHub
|
| 22 |
GITHUB_REPO: process.env.GITHUB_REPO || 'NLarchive/my-webapp-hf',
|
| 23 |
GITHUB_OWNER: process.env.GITHUB_OWNER || 'NLarchive',
|
|
|
|
| 38 |
};
|
| 39 |
|
| 40 |
export function validateConfig() {
|
| 41 |
+
const required = ['GEMINI_API_KEY'];
|
| 42 |
+
const githubAuth = ['GITHUB_TOKEN', 'GH_APP_ID', 'GH_APP_PRIVATE_KEY_B64', 'GH_APP_INSTALLATION_ID'];
|
| 43 |
+
|
| 44 |
const missing = required.filter(key => !config[key]);
|
| 45 |
|
| 46 |
+
// Check if at least one GitHub auth method is configured
|
| 47 |
+
const hasGitHubAuth = githubAuth.some(key => config[key]);
|
| 48 |
+
if (!hasGitHubAuth) {
|
| 49 |
+
missing.push('GitHub authentication (GITHUB_TOKEN or GH_APP_* variables)');
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
if (missing.length > 0) {
|
| 53 |
console.error(`Missing required environment variables: ${missing.join(', ')}`);
|
| 54 |
process.exit(1);
|
|
@@ -14,6 +14,7 @@ import { scannerAgent } from './agents/scanner.js';
|
|
| 14 |
import chatRoutes from './api/chat.js';
|
| 15 |
import scannerRoutes from './api/scanner.js';
|
| 16 |
import projectRoutes from './api/project.js';
|
|
|
|
| 17 |
|
| 18 |
const app = express();
|
| 19 |
|
|
@@ -41,6 +42,7 @@ app.get('/health', (req, res) => {
|
|
| 41 |
app.use('/api/chat', chatRoutes);
|
| 42 |
app.use('/api/scanner', scannerRoutes);
|
| 43 |
app.use('/api/project', projectRoutes);
|
|
|
|
| 44 |
|
| 45 |
// Serve static files (HTML, CSS, JS)
|
| 46 |
app.use(express.static('.'));
|
|
@@ -66,16 +68,25 @@ async function startServer() {
|
|
| 66 |
validateConfig();
|
| 67 |
|
| 68 |
// Start server
|
| 69 |
-
app.listen(config.PORT, () => {
|
| 70 |
logger.info(`Server started on port ${config.PORT}`);
|
|
|
|
|
|
|
| 71 |
});
|
| 72 |
|
| 73 |
// Start scanner if enabled
|
| 74 |
if (config.ENABLE_AUTO_FIX) {
|
|
|
|
| 75 |
scannerAgent.startPeriodicScanning();
|
| 76 |
} else {
|
| 77 |
-
logger.info('Auto-fix is disabled');
|
| 78 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
} catch (error) {
|
| 80 |
logger.error('Failed to start server', { error: error.message });
|
| 81 |
process.exit(1);
|
|
|
|
| 14 |
import chatRoutes from './api/chat.js';
|
| 15 |
import scannerRoutes from './api/scanner.js';
|
| 16 |
import projectRoutes from './api/project.js';
|
| 17 |
+
import docsRoutes from './api/docs.js';
|
| 18 |
|
| 19 |
const app = express();
|
| 20 |
|
|
|
|
| 42 |
app.use('/api/chat', chatRoutes);
|
| 43 |
app.use('/api/scanner', scannerRoutes);
|
| 44 |
app.use('/api/project', projectRoutes);
|
| 45 |
+
app.use('/api/docs', docsRoutes);
|
| 46 |
|
| 47 |
// Serve static files (HTML, CSS, JS)
|
| 48 |
app.use(express.static('.'));
|
|
|
|
| 68 |
validateConfig();
|
| 69 |
|
| 70 |
// Start server
|
| 71 |
+
const server = app.listen(config.PORT, () => {
|
| 72 |
logger.info(`Server started on port ${config.PORT}`);
|
| 73 |
+
logger.info(`Environment: ${config.NODE_ENV}`);
|
| 74 |
+
logger.info(`Health check: http://localhost:${config.PORT}/health`);
|
| 75 |
});
|
| 76 |
|
| 77 |
// Start scanner if enabled
|
| 78 |
if (config.ENABLE_AUTO_FIX) {
|
| 79 |
+
logger.info('Starting periodic scanner...');
|
| 80 |
scannerAgent.startPeriodicScanning();
|
| 81 |
} else {
|
| 82 |
+
logger.info('Auto-fix is disabled (ENABLE_AUTO_FIX=false)');
|
| 83 |
}
|
| 84 |
+
|
| 85 |
+
// Handle server errors
|
| 86 |
+
server.on('error', (err) => {
|
| 87 |
+
logger.error('Server error', { error: err.message });
|
| 88 |
+
process.exit(1);
|
| 89 |
+
});
|
| 90 |
} catch (error) {
|
| 91 |
logger.error('Failed to start server', { error: error.message });
|
| 92 |
process.exit(1);
|
|
@@ -3,19 +3,51 @@
|
|
| 3 |
* Handles GitHub API interactions (PR creation, issue creation, commits, etc.)
|
| 4 |
*/
|
| 5 |
|
| 6 |
-
import
|
|
|
|
| 7 |
import { config } from '../config/env.js';
|
| 8 |
import { logger } from '../config/logger.js';
|
| 9 |
|
| 10 |
class GitHubService {
|
| 11 |
constructor() {
|
| 12 |
-
this.
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
}
|
| 20 |
|
| 21 |
/**
|
|
@@ -26,11 +58,15 @@ class GitHubService {
|
|
| 26 |
async createIssue(issue) {
|
| 27 |
try {
|
| 28 |
const { title, body, labels = [], assignees = [] } = issue;
|
| 29 |
-
|
| 30 |
-
const response = await this.
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
logger.info('GitHub issue created', { issueNumber: response.data.number });
|
| 36 |
return response.data;
|
|
@@ -55,10 +91,15 @@ class GitHubService {
|
|
| 55 |
draft = false,
|
| 56 |
} = pr;
|
| 57 |
|
| 58 |
-
const response = await this.
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
logger.info('GitHub PR created', { prNumber: response.data.number });
|
| 64 |
return response.data;
|
|
@@ -75,9 +116,11 @@ class GitHubService {
|
|
| 75 |
*/
|
| 76 |
async getFileContents(path = '') {
|
| 77 |
try {
|
| 78 |
-
const response = await this.
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
| 81 |
|
| 82 |
return response.data;
|
| 83 |
} catch (error) {
|
|
@@ -113,13 +156,13 @@ class GitHubService {
|
|
| 113 |
*/
|
| 114 |
async triggerWorkflow(workflowId, inputs = {}) {
|
| 115 |
try {
|
| 116 |
-
const response = await this.
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
);
|
| 123 |
|
| 124 |
logger.info('Workflow triggered', { workflowId });
|
| 125 |
return response.data;
|
|
@@ -135,7 +178,10 @@ class GitHubService {
|
|
| 135 |
*/
|
| 136 |
async getRepoInfo() {
|
| 137 |
try {
|
| 138 |
-
const response = await this.
|
|
|
|
|
|
|
|
|
|
| 139 |
return response.data;
|
| 140 |
} catch (error) {
|
| 141 |
logger.error('Failed to get repo info', { error: error.message });
|
|
@@ -150,10 +196,11 @@ class GitHubService {
|
|
| 150 |
*/
|
| 151 |
async listIssues(state = 'open') {
|
| 152 |
try {
|
| 153 |
-
const response = await this.
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
|
|
|
| 157 |
return response.data;
|
| 158 |
} catch (error) {
|
| 159 |
logger.error('Failed to list issues', { error: error.message });
|
|
@@ -169,10 +216,12 @@ class GitHubService {
|
|
| 169 |
*/
|
| 170 |
async addIssueComment(issueNumber, body) {
|
| 171 |
try {
|
| 172 |
-
const response = await this.
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
|
|
|
|
|
|
| 176 |
|
| 177 |
logger.info('Issue comment added', { issueNumber });
|
| 178 |
return response.data;
|
|
|
|
| 3 |
* Handles GitHub API interactions (PR creation, issue creation, commits, etc.)
|
| 4 |
*/
|
| 5 |
|
| 6 |
+
import { Octokit } from '@octokit/rest';
|
| 7 |
+
import { createAppAuth } from '@octokit/auth-app';
|
| 8 |
import { config } from '../config/env.js';
|
| 9 |
import { logger } from '../config/logger.js';
|
| 10 |
|
| 11 |
class GitHubService {
|
| 12 |
constructor() {
|
| 13 |
+
this.octokit = null;
|
| 14 |
+
this.initializeOctokit();
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
async initializeOctokit() {
|
| 18 |
+
try {
|
| 19 |
+
// Try GitHub App authentication first
|
| 20 |
+
if (config.GH_APP_ID && config.GH_APP_PRIVATE_KEY_B64 && config.GH_APP_INSTALLATION_ID) {
|
| 21 |
+
const privateKey = Buffer.from(config.GH_APP_PRIVATE_KEY_B64, 'base64').toString('utf8');
|
| 22 |
+
|
| 23 |
+
const auth = createAppAuth({
|
| 24 |
+
appId: config.GH_APP_ID,
|
| 25 |
+
privateKey,
|
| 26 |
+
installationId: config.GH_APP_INSTALLATION_ID,
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
this.octokit = new Octokit({
|
| 30 |
+
authStrategy: auth,
|
| 31 |
+
auth: { type: 'installation' },
|
| 32 |
+
});
|
| 33 |
+
|
| 34 |
+
logger.info('GitHub App authentication initialized');
|
| 35 |
+
}
|
| 36 |
+
// Fallback to personal access token
|
| 37 |
+
else if (config.GITHUB_TOKEN) {
|
| 38 |
+
this.octokit = new Octokit({
|
| 39 |
+
auth: config.GITHUB_TOKEN,
|
| 40 |
+
});
|
| 41 |
+
|
| 42 |
+
logger.info('GitHub token authentication initialized');
|
| 43 |
+
}
|
| 44 |
+
else {
|
| 45 |
+
throw new Error('No GitHub authentication method configured');
|
| 46 |
+
}
|
| 47 |
+
} catch (error) {
|
| 48 |
+
logger.error('Failed to initialize GitHub client', { error: error.message });
|
| 49 |
+
throw error;
|
| 50 |
+
}
|
| 51 |
}
|
| 52 |
|
| 53 |
/**
|
|
|
|
| 58 |
async createIssue(issue) {
|
| 59 |
try {
|
| 60 |
const { title, body, labels = [], assignees = [] } = issue;
|
| 61 |
+
|
| 62 |
+
const response = await this.octokit.issues.create({
|
| 63 |
+
owner: config.GITHUB_OWNER,
|
| 64 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 65 |
+
title,
|
| 66 |
+
body,
|
| 67 |
+
labels,
|
| 68 |
+
assignees,
|
| 69 |
+
});
|
| 70 |
|
| 71 |
logger.info('GitHub issue created', { issueNumber: response.data.number });
|
| 72 |
return response.data;
|
|
|
|
| 91 |
draft = false,
|
| 92 |
} = pr;
|
| 93 |
|
| 94 |
+
const response = await this.octokit.pulls.create({
|
| 95 |
+
owner: config.GITHUB_OWNER,
|
| 96 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 97 |
+
title,
|
| 98 |
+
body,
|
| 99 |
+
head,
|
| 100 |
+
base,
|
| 101 |
+
draft,
|
| 102 |
+
});
|
| 103 |
|
| 104 |
logger.info('GitHub PR created', { prNumber: response.data.number });
|
| 105 |
return response.data;
|
|
|
|
| 116 |
*/
|
| 117 |
async getFileContents(path = '') {
|
| 118 |
try {
|
| 119 |
+
const response = await this.octokit.repos.getContent({
|
| 120 |
+
owner: config.GITHUB_OWNER,
|
| 121 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 122 |
+
path,
|
| 123 |
+
});
|
| 124 |
|
| 125 |
return response.data;
|
| 126 |
} catch (error) {
|
|
|
|
| 156 |
*/
|
| 157 |
async triggerWorkflow(workflowId, inputs = {}) {
|
| 158 |
try {
|
| 159 |
+
const response = await this.octokit.actions.createWorkflowDispatch({
|
| 160 |
+
owner: config.GITHUB_OWNER,
|
| 161 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 162 |
+
workflow_id: workflowId,
|
| 163 |
+
ref: config.GITHUB_BRANCH,
|
| 164 |
+
inputs,
|
| 165 |
+
});
|
| 166 |
|
| 167 |
logger.info('Workflow triggered', { workflowId });
|
| 168 |
return response.data;
|
|
|
|
| 178 |
*/
|
| 179 |
async getRepoInfo() {
|
| 180 |
try {
|
| 181 |
+
const response = await this.octokit.repos.get({
|
| 182 |
+
owner: config.GITHUB_OWNER,
|
| 183 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 184 |
+
});
|
| 185 |
return response.data;
|
| 186 |
} catch (error) {
|
| 187 |
logger.error('Failed to get repo info', { error: error.message });
|
|
|
|
| 196 |
*/
|
| 197 |
async listIssues(state = 'open') {
|
| 198 |
try {
|
| 199 |
+
const response = await this.octokit.issues.listForRepo({
|
| 200 |
+
owner: config.GITHUB_OWNER,
|
| 201 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 202 |
+
state,
|
| 203 |
+
});
|
| 204 |
return response.data;
|
| 205 |
} catch (error) {
|
| 206 |
logger.error('Failed to list issues', { error: error.message });
|
|
|
|
| 216 |
*/
|
| 217 |
async addIssueComment(issueNumber, body) {
|
| 218 |
try {
|
| 219 |
+
const response = await this.octokit.issues.createComment({
|
| 220 |
+
owner: config.GITHUB_OWNER,
|
| 221 |
+
repo: config.GITHUB_REPO.split('/')[1],
|
| 222 |
+
issue_number: issueNumber,
|
| 223 |
+
body,
|
| 224 |
+
});
|
| 225 |
|
| 226 |
logger.info('Issue comment added', { issueNumber });
|
| 227 |
return response.data;
|