feat: add GitHub service for PAT validation, forking, and PR management
This commit is contained in:
Generated
+238
@@ -852,6 +852,21 @@
|
|||||||
"url": "https://opencollective.com/js-sdsl"
|
"url": "https://opencollective.com/js-sdsl"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kwsites/file-exists": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kwsites/promise-deferred": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@layonara-forge/backend": {
|
"node_modules/@layonara-forge/backend": {
|
||||||
"resolved": "packages/backend",
|
"resolved": "packages/backend",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -883,6 +898,161 @@
|
|||||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@octokit/auth-token": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/core": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/auth-token": "^6.0.0",
|
||||||
|
"@octokit/graphql": "^9.0.3",
|
||||||
|
"@octokit/request": "^10.0.6",
|
||||||
|
"@octokit/request-error": "^7.0.2",
|
||||||
|
"@octokit/types": "^16.0.0",
|
||||||
|
"before-after-hook": "^4.0.0",
|
||||||
|
"universal-user-agent": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/endpoint": {
|
||||||
|
"version": "11.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz",
|
||||||
|
"integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/types": "^16.0.0",
|
||||||
|
"universal-user-agent": "^7.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/graphql": {
|
||||||
|
"version": "9.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
|
||||||
|
"integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/request": "^10.0.6",
|
||||||
|
"@octokit/types": "^16.0.0",
|
||||||
|
"universal-user-agent": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/openapi-types": {
|
||||||
|
"version": "27.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
|
||||||
|
"integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/plugin-paginate-rest": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/types": "^16.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@octokit/core": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/plugin-request-log": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@octokit/core": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||||
|
"version": "17.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz",
|
||||||
|
"integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/types": "^16.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@octokit/core": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/request": {
|
||||||
|
"version": "10.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz",
|
||||||
|
"integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/endpoint": "^11.0.3",
|
||||||
|
"@octokit/request-error": "^7.0.2",
|
||||||
|
"@octokit/types": "^16.0.0",
|
||||||
|
"fast-content-type-parse": "^3.0.0",
|
||||||
|
"json-with-bigint": "^3.5.3",
|
||||||
|
"universal-user-agent": "^7.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/request-error": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/types": "^16.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/rest": {
|
||||||
|
"version": "22.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz",
|
||||||
|
"integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/core": "^7.0.6",
|
||||||
|
"@octokit/plugin-paginate-rest": "^14.0.0",
|
||||||
|
"@octokit/plugin-request-log": "^6.0.0",
|
||||||
|
"@octokit/plugin-rest-endpoint-methods": "^17.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@octokit/types": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/openapi-types": "^27.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@protobufjs/aspromise": {
|
"node_modules/@protobufjs/aspromise": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
@@ -1343,6 +1513,21 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@simple-git/args-pathspec": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@simple-git/argv-parser": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@simple-git/args-pathspec": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
@@ -1856,6 +2041,12 @@
|
|||||||
"tweetnacl": "^0.14.3"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/before-after-hook": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/bl": {
|
"node_modules/bl": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||||
@@ -2542,6 +2733,22 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-content-type-parse": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fdir": {
|
"node_modules/fdir": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
@@ -2850,6 +3057,12 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/json-with-bigint": {
|
||||||
|
"version": "3.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz",
|
||||||
|
"integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
@@ -3636,6 +3849,23 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-git": {
|
||||||
|
"version": "3.36.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz",
|
||||||
|
"integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kwsites/file-exists": "^1.1.1",
|
||||||
|
"@kwsites/promise-deferred": "^1.1.1",
|
||||||
|
"@simple-git/args-pathspec": "^1.0.3",
|
||||||
|
"@simple-git/argv-parser": "^1.1.0",
|
||||||
|
"debug": "^4.4.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@@ -3951,6 +4181,12 @@
|
|||||||
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/universal-user-agent": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
@@ -4829,10 +5065,12 @@
|
|||||||
"name": "@layonara-forge/backend",
|
"name": "@layonara-forge/backend",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@octokit/rest": "^22.0.1",
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dockerode": "^4.0.0",
|
"dockerode": "^4.0.0",
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
|
"simple-git": "^3.36.0",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -10,17 +10,19 @@
|
|||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"@octokit/rest": "^22.0.1",
|
||||||
"dockerode": "^4.0.0",
|
|
||||||
"ws": "^8.18.0",
|
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"cors": "^2.8.5"
|
"cors": "^2.8.5",
|
||||||
|
"dockerode": "^4.0.0",
|
||||||
|
"express": "^5.0.0",
|
||||||
|
"simple-git": "^3.36.0",
|
||||||
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^5.0.0",
|
|
||||||
"@types/dockerode": "^3.3.0",
|
|
||||||
"@types/ws": "^8.5.0",
|
|
||||||
"@types/cors": "^2.8.0",
|
"@types/cors": "^2.8.0",
|
||||||
|
"@types/dockerode": "^3.3.0",
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
|
"@types/ws": "^8.5.0",
|
||||||
"tsx": "^4.19.0",
|
"tsx": "^4.19.0",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export const REPOS = [
|
||||||
|
{ name: "nwn-module", upstream: "layonara/nwn-module", branch: "ee" },
|
||||||
|
{ name: "nwn-haks", upstream: "layonara/nwn-haks", branch: "64bit" },
|
||||||
|
{ name: "unified", upstream: "plenarius/unified", branch: "master" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type RepoName = (typeof REPOS)[number]["name"];
|
||||||
@@ -12,6 +12,7 @@ import terminalRouter from "./routes/terminal.js";
|
|||||||
import buildRouter from "./routes/build.js";
|
import buildRouter from "./routes/build.js";
|
||||||
import serverRouter from "./routes/server.js";
|
import serverRouter from "./routes/server.js";
|
||||||
import toolsetRouter from "./routes/toolset.js";
|
import toolsetRouter from "./routes/toolset.js";
|
||||||
|
import githubRouter from "./routes/github.js";
|
||||||
import { attachWebSocket, createTerminalSession } from "./services/terminal.service.js";
|
import { attachWebSocket, createTerminalSession } from "./services/terminal.service.js";
|
||||||
import { attachLspWebSocket } from "./services/lsp.service.js";
|
import { attachLspWebSocket } from "./services/lsp.service.js";
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ app.use("/api/terminal", terminalRouter);
|
|||||||
app.use("/api/build", buildRouter);
|
app.use("/api/build", buildRouter);
|
||||||
app.use("/api/server", serverRouter);
|
app.use("/api/server", serverRouter);
|
||||||
app.use("/api/toolset", toolsetRouter);
|
app.use("/api/toolset", toolsetRouter);
|
||||||
|
app.use("/api/github", githubRouter);
|
||||||
|
|
||||||
const frontendDist = path.resolve(__dirname, "../../frontend/dist");
|
const frontendDist = path.resolve(__dirname, "../../frontend/dist");
|
||||||
app.use(express.static(frontendDist));
|
app.use(express.static(frontendDist));
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { readConfig, writeConfig } from "../services/workspace.service.js";
|
||||||
|
import { REPOS } from "../config/repos.js";
|
||||||
|
import {
|
||||||
|
validatePat,
|
||||||
|
forkRepo,
|
||||||
|
listUserForks,
|
||||||
|
createPullRequest,
|
||||||
|
listPullRequests,
|
||||||
|
} from "../services/github.service.js";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
async function getPat(): Promise<string> {
|
||||||
|
const config = await readConfig();
|
||||||
|
const pat = config.githubPat;
|
||||||
|
if (!pat) throw new Error("GitHub PAT not configured");
|
||||||
|
return pat;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post("/validate-pat", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { pat } = req.body;
|
||||||
|
if (!pat) {
|
||||||
|
res.status(400).json({ error: "PAT is required" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await validatePat(pat);
|
||||||
|
|
||||||
|
const config = await readConfig();
|
||||||
|
await writeConfig({ ...config, githubPat: pat });
|
||||||
|
|
||||||
|
res.json(result);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : "Invalid PAT";
|
||||||
|
res.status(401).json({ error: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/fork", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const pat = await getPat();
|
||||||
|
const { repo: repoName } = req.body;
|
||||||
|
const repoDef = REPOS.find((r) => r.name === repoName);
|
||||||
|
if (!repoDef) {
|
||||||
|
res.status(400).json({ error: `Unknown repo: ${repoName}` });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [owner, repo] = repoDef.upstream.split("/");
|
||||||
|
const result = await forkRepo(pat, owner, repo);
|
||||||
|
res.json(result);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : "Fork failed";
|
||||||
|
res.status(500).json({ error: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/forks", async (_req, res) => {
|
||||||
|
try {
|
||||||
|
const pat = await getPat();
|
||||||
|
const forks = await listUserForks(pat);
|
||||||
|
res.json(forks);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : "Failed to list forks";
|
||||||
|
res.status(500).json({ error: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/pr", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const pat = await getPat();
|
||||||
|
const { repo: repoName, title, body, headBranch } = req.body;
|
||||||
|
const repoDef = REPOS.find((r) => r.name === repoName);
|
||||||
|
if (!repoDef) {
|
||||||
|
res.status(400).json({ error: `Unknown repo: ${repoName}` });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { login } = await import("../services/github.service.js").then((m) =>
|
||||||
|
m.validatePat(pat),
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await createPullRequest(pat, {
|
||||||
|
upstream: repoDef.upstream,
|
||||||
|
repo: repoName,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
head: `${login}:${headBranch}`,
|
||||||
|
base: repoDef.branch,
|
||||||
|
});
|
||||||
|
res.json(result);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : "PR creation failed";
|
||||||
|
res.status(500).json({ error: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/prs/:repo", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const pat = await getPat();
|
||||||
|
const repoName = req.params.repo;
|
||||||
|
const repoDef = REPOS.find((r) => r.name === repoName);
|
||||||
|
if (!repoDef) {
|
||||||
|
res.status(400).json({ error: `Unknown repo: ${repoName}` });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [owner, repo] = repoDef.upstream.split("/");
|
||||||
|
const prs = await listPullRequests(pat, owner, repo);
|
||||||
|
res.json(prs);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : "Failed to list PRs";
|
||||||
|
res.status(500).json({ error: message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { Octokit } from "@octokit/rest";
|
||||||
|
import { REPOS } from "../config/repos.js";
|
||||||
|
|
||||||
|
export async function validatePat(pat: string) {
|
||||||
|
const octokit = new Octokit({ auth: pat });
|
||||||
|
const { data, headers } = await octokit.rest.users.getAuthenticated();
|
||||||
|
const scopes = (headers["x-oauth-scopes"] || "")
|
||||||
|
.split(",")
|
||||||
|
.map((s: string) => s.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
return { login: data.login, scopes };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function forkRepo(pat: string, owner: string, repo: string) {
|
||||||
|
const octokit = new Octokit({ auth: pat });
|
||||||
|
const { data } = await octokit.rest.repos.createFork({ owner, repo });
|
||||||
|
return { fullName: data.full_name, cloneUrl: data.clone_url };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listUserForks(pat: string) {
|
||||||
|
const octokit = new Octokit({ auth: pat });
|
||||||
|
const { data: user } = await octokit.rest.users.getAuthenticated();
|
||||||
|
const login = user.login;
|
||||||
|
|
||||||
|
const results: Array<{ repo: string; forked: boolean; fullName?: string }> = [];
|
||||||
|
|
||||||
|
for (const repo of REPOS) {
|
||||||
|
try {
|
||||||
|
const { data } = await octokit.rest.repos.get({
|
||||||
|
owner: login,
|
||||||
|
repo: repo.name,
|
||||||
|
});
|
||||||
|
results.push({ repo: repo.name, forked: true, fullName: data.full_name });
|
||||||
|
} catch {
|
||||||
|
results.push({ repo: repo.name, forked: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPullRequest(
|
||||||
|
pat: string,
|
||||||
|
opts: { upstream: string; repo: string; title: string; body: string; head: string; base: string },
|
||||||
|
) {
|
||||||
|
const octokit = new Octokit({ auth: pat });
|
||||||
|
const [owner, repo] = opts.upstream.split("/");
|
||||||
|
const { data } = await octokit.rest.pulls.create({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
title: opts.title,
|
||||||
|
body: opts.body,
|
||||||
|
head: opts.head,
|
||||||
|
base: opts.base,
|
||||||
|
});
|
||||||
|
return { number: data.number, url: data.html_url };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listPullRequests(pat: string, owner: string, repo: string) {
|
||||||
|
const octokit = new Octokit({ auth: pat });
|
||||||
|
const { data } = await octokit.rest.pulls.list({ owner, repo, state: "open" });
|
||||||
|
return data.map((pr) => ({
|
||||||
|
number: pr.number,
|
||||||
|
title: pr.title,
|
||||||
|
state: pr.state,
|
||||||
|
url: pr.html_url,
|
||||||
|
user: pr.user?.login || "",
|
||||||
|
createdAt: pr.created_at,
|
||||||
|
}));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user