Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96a66fc857 | ||
|
|
3272b9db76 | ||
|
|
06edc60b55 | ||
|
|
14158047ac | ||
|
|
e5fb28ef31 |
@@ -1,12 +1,12 @@
|
|||||||
name: Release
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
pull_request:
|
||||||
- "v*.*.*"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
checks:
|
||||||
|
name: Checks
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: https://github.com/actions/checkout@v4
|
- uses: https://github.com/actions/checkout@v4
|
||||||
@@ -26,6 +26,24 @@ jobs:
|
|||||||
- run: pnpm run format:check
|
- run: pnpm run format:check
|
||||||
- run: pnpm run test
|
- run: pnpm run test
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: Publish to npm registry
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
needs: checks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: https://github.com/actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: https://github.com/pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.21.0
|
||||||
|
|
||||||
|
- uses: https://github.com/actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Set package version from the tag
|
- name: Set package version from the tag
|
||||||
run: npm pkg set version="${GITHUB_REF_NAME#v}"
|
run: npm pkg set version="${GITHUB_REF_NAME#v}"
|
||||||
|
|
||||||
+2
-2
@@ -7,8 +7,8 @@ When `auth.enabled: true`, every endpoint below except `/api/healthz`, `/api/aut
|
|||||||
header using a token from `triggershell users add-token`.
|
header using a token from `triggershell users add-token`.
|
||||||
|
|
||||||
`triggershell run <scriptId>` is a first-party client of this exact REST + WS contract (see
|
`triggershell run <scriptId>` is a first-party client of this exact REST + WS contract (see
|
||||||
[Running scripts from the CLI](../README.md#running-scripts-from-the-cli)) - nothing below is
|
[Running scripts from the CLI](https://dev.pivoine.art/valknar/triggershell/src/branch/main/README.md#running-scripts-from-the-cli)) -
|
||||||
CLI-specific.
|
nothing below is CLI-specific.
|
||||||
|
|
||||||
## Auth
|
## Auth
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "triggershell",
|
"name": "triggershell",
|
||||||
"version": "0.1.0",
|
"version": "1.0.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
"lucide-react": "^1.31.0",
|
"lucide-react": "^1.31.0",
|
||||||
"next": "16.3.1",
|
"next": "16.3.1",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
"pino": "^10.3.1",
|
||||||
"react": "19.2.8",
|
"react": "19.2.8",
|
||||||
"react-dom": "19.2.8",
|
"react-dom": "19.2.8",
|
||||||
"react-hook-form": "^7.85.0",
|
"react-hook-form": "^7.85.0",
|
||||||
@@ -86,6 +87,7 @@
|
|||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.3.1",
|
"eslint-config-next": "16.3.1",
|
||||||
|
"pino-pretty": "^13.1.3",
|
||||||
"prettier": "^3.9.6",
|
"prettier": "^3.9.6",
|
||||||
"prettier-plugin-tailwindcss": "^0.8.1",
|
"prettier-plugin-tailwindcss": "^0.8.1",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
|
|||||||
Generated
+178
@@ -59,6 +59,9 @@ importers:
|
|||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.4.6
|
specifier: ^0.4.6
|
||||||
version: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
version: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
||||||
|
pino:
|
||||||
|
specifier: ^10.3.1
|
||||||
|
version: 10.3.1
|
||||||
react:
|
react:
|
||||||
specifier: 19.2.8
|
specifier: 19.2.8
|
||||||
version: 19.2.8
|
version: 19.2.8
|
||||||
@@ -132,6 +135,9 @@ importers:
|
|||||||
eslint-config-next:
|
eslint-config-next:
|
||||||
specifier: 16.3.1
|
specifier: 16.3.1
|
||||||
version: 16.3.1(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)
|
version: 16.3.1(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)
|
||||||
|
pino-pretty:
|
||||||
|
specifier: ^13.1.3
|
||||||
|
version: 13.1.3
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.9.6
|
specifier: ^3.9.6
|
||||||
version: 3.9.6
|
version: 3.9.6
|
||||||
@@ -1358,6 +1364,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
|
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
'@pinojs/redact@0.4.0':
|
||||||
|
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.1.7':
|
'@radix-ui/primitive@1.1.7':
|
||||||
resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==}
|
resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==}
|
||||||
|
|
||||||
@@ -1987,6 +1996,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
atomic-sleep@1.0.0:
|
||||||
|
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
atomically@1.7.0:
|
atomically@1.7.0:
|
||||||
resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==}
|
resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==}
|
||||||
engines: {node: '>=10.12.0'}
|
engines: {node: '>=10.12.0'}
|
||||||
@@ -2136,6 +2149,9 @@ packages:
|
|||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
colorette@2.0.20:
|
||||||
|
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||||
|
|
||||||
comma-separated-tokens@2.0.3:
|
comma-separated-tokens@2.0.3:
|
||||||
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
|
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
|
||||||
|
|
||||||
@@ -2226,6 +2242,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
|
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
dateformat@4.6.3:
|
||||||
|
resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
|
||||||
|
|
||||||
debounce-fn@4.0.0:
|
debounce-fn@4.0.0:
|
||||||
resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==}
|
resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2439,6 +2458,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
end-of-stream@1.4.5:
|
||||||
|
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||||
|
|
||||||
enhanced-resolve@5.24.5:
|
enhanced-resolve@5.24.5:
|
||||||
resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==}
|
resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -2685,6 +2707,9 @@ packages:
|
|||||||
extend@3.0.2:
|
extend@3.0.2:
|
||||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||||
|
|
||||||
|
fast-copy@4.0.4:
|
||||||
|
resolution: {integrity: sha512-eVAiWVNPSEGIzDl5yPuLrx8fNMogScXvD9xp1Kzd41FjRIz2I3sSIcxsFeM5EzFfHAfobdvs8ZySffUopljvIA==}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3:
|
fast-deep-equal@3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
@@ -2702,6 +2727,9 @@ packages:
|
|||||||
fast-levenshtein@2.0.6:
|
fast-levenshtein@2.0.6:
|
||||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||||
|
|
||||||
|
fast-safe-stringify@2.1.1:
|
||||||
|
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
||||||
|
|
||||||
fast-string-truncated-width@3.0.3:
|
fast-string-truncated-width@3.0.3:
|
||||||
resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
|
resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
|
||||||
|
|
||||||
@@ -2894,6 +2922,9 @@ packages:
|
|||||||
hast-util-whitespace@3.0.0:
|
hast-util-whitespace@3.0.0:
|
||||||
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
|
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
|
||||||
|
|
||||||
|
help-me@5.0.0:
|
||||||
|
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
||||||
|
|
||||||
hermes-estree@0.25.1:
|
hermes-estree@0.25.1:
|
||||||
resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
|
resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
|
||||||
|
|
||||||
@@ -3172,6 +3203,10 @@ packages:
|
|||||||
jose@6.2.8:
|
jose@6.2.8:
|
||||||
resolution: {integrity: sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==}
|
resolution: {integrity: sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==}
|
||||||
|
|
||||||
|
joycon@3.1.1:
|
||||||
|
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@@ -3649,6 +3684,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
|
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
on-exit-leak-free@2.1.2:
|
||||||
|
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
on-finished@2.4.1:
|
on-finished@2.4.1:
|
||||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -3759,6 +3798,20 @@ packages:
|
|||||||
resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
|
resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
pino-abstract-transport@3.0.0:
|
||||||
|
resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==}
|
||||||
|
|
||||||
|
pino-pretty@13.1.3:
|
||||||
|
resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
pino-std-serializers@7.1.0:
|
||||||
|
resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==}
|
||||||
|
|
||||||
|
pino@10.3.1:
|
||||||
|
resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
pkce-challenge@5.0.1:
|
pkce-challenge@5.0.1:
|
||||||
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
|
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
|
||||||
engines: {node: '>=16.20.0'}
|
engines: {node: '>=16.20.0'}
|
||||||
@@ -3863,6 +3916,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
|
resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
process-warning@5.1.0:
|
||||||
|
resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==}
|
||||||
|
|
||||||
prompts@2.4.2:
|
prompts@2.4.2:
|
||||||
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -3877,6 +3933,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
pump@3.0.4:
|
||||||
|
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
||||||
|
|
||||||
punycode@2.3.1:
|
punycode@2.3.1:
|
||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3888,6 +3947,9 @@ packages:
|
|||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
quick-format-unescaped@4.0.4:
|
||||||
|
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
|
||||||
|
|
||||||
range-parser@1.3.0:
|
range-parser@1.3.0:
|
||||||
resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==}
|
resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -3950,6 +4012,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==}
|
resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
real-require@0.2.0:
|
||||||
|
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
||||||
|
engines: {node: '>= 12.13.0'}
|
||||||
|
|
||||||
|
real-require@1.0.0:
|
||||||
|
resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==}
|
||||||
|
|
||||||
recast@0.23.21:
|
recast@0.23.21:
|
||||||
resolution: {integrity: sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==}
|
resolution: {integrity: sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
@@ -4024,12 +4093,19 @@ packages:
|
|||||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
safe-stable-stringify@2.5.0:
|
||||||
|
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
safer-buffer@2.1.2:
|
safer-buffer@2.1.2:
|
||||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
|
||||||
scheduler@0.27.0:
|
scheduler@0.27.0:
|
||||||
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
|
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
|
||||||
|
|
||||||
|
secure-json-parse@4.1.0:
|
||||||
|
resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==}
|
||||||
|
|
||||||
semver@6.3.1:
|
semver@6.3.1:
|
||||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -4118,6 +4194,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==}
|
resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==}
|
||||||
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
||||||
|
|
||||||
|
sonic-boom@4.2.1:
|
||||||
|
resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
|
||||||
|
|
||||||
sonner@2.0.8:
|
sonner@2.0.8:
|
||||||
resolution: {integrity: sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==}
|
resolution: {integrity: sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -4142,6 +4221,10 @@ packages:
|
|||||||
space-separated-tokens@2.0.2:
|
space-separated-tokens@2.0.2:
|
||||||
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||||
|
|
||||||
|
split2@4.2.0:
|
||||||
|
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||||
|
engines: {node: '>= 10.x'}
|
||||||
|
|
||||||
stable-hash@0.0.5:
|
stable-hash@0.0.5:
|
||||||
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
||||||
|
|
||||||
@@ -4215,6 +4298,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-json-comments@5.0.3:
|
||||||
|
resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
style-to-js@1.1.21:
|
style-to-js@1.1.21:
|
||||||
resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
|
resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
|
||||||
|
|
||||||
@@ -4258,6 +4345,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
thread-stream@4.2.0:
|
||||||
|
resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
tiny-invariant@1.3.3:
|
tiny-invariant@1.3.3:
|
||||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
|
||||||
@@ -5447,6 +5538,8 @@ snapshots:
|
|||||||
|
|
||||||
'@phc/format@1.0.0': {}
|
'@phc/format@1.0.0': {}
|
||||||
|
|
||||||
|
'@pinojs/redact@0.4.0': {}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.1.7': {}
|
'@radix-ui/primitive@1.1.7': {}
|
||||||
|
|
||||||
'@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.18)(react@19.2.8)':
|
'@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.18)(react@19.2.8)':
|
||||||
@@ -6031,6 +6124,8 @@ snapshots:
|
|||||||
|
|
||||||
async-function@1.0.0: {}
|
async-function@1.0.0: {}
|
||||||
|
|
||||||
|
atomic-sleep@1.0.0: {}
|
||||||
|
|
||||||
atomically@1.7.0: {}
|
atomically@1.7.0: {}
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
@@ -6172,6 +6267,8 @@ snapshots:
|
|||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
|
colorette@2.0.20: {}
|
||||||
|
|
||||||
comma-separated-tokens@2.0.3: {}
|
comma-separated-tokens@2.0.3: {}
|
||||||
|
|
||||||
commander@11.1.0: {}
|
commander@11.1.0: {}
|
||||||
@@ -6256,6 +6353,8 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
is-data-view: 1.0.2
|
is-data-view: 1.0.2
|
||||||
|
|
||||||
|
dateformat@4.6.3: {}
|
||||||
|
|
||||||
debounce-fn@4.0.0:
|
debounce-fn@4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn: 3.1.0
|
mimic-fn: 3.1.0
|
||||||
@@ -6357,6 +6456,10 @@ snapshots:
|
|||||||
|
|
||||||
encodeurl@2.0.0: {}
|
encodeurl@2.0.0: {}
|
||||||
|
|
||||||
|
end-of-stream@1.4.5:
|
||||||
|
dependencies:
|
||||||
|
once: 1.4.0
|
||||||
|
|
||||||
enhanced-resolve@5.24.5:
|
enhanced-resolve@5.24.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@@ -6877,6 +6980,8 @@ snapshots:
|
|||||||
|
|
||||||
extend@3.0.2: {}
|
extend@3.0.2: {}
|
||||||
|
|
||||||
|
fast-copy@4.0.4: {}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
fast-deep-equal@3.1.3: {}
|
||||||
|
|
||||||
fast-glob@3.3.1:
|
fast-glob@3.3.1:
|
||||||
@@ -6899,6 +7004,8 @@ snapshots:
|
|||||||
|
|
||||||
fast-levenshtein@2.0.6: {}
|
fast-levenshtein@2.0.6: {}
|
||||||
|
|
||||||
|
fast-safe-stringify@2.1.1: {}
|
||||||
|
|
||||||
fast-string-truncated-width@3.0.3: {}
|
fast-string-truncated-width@3.0.3: {}
|
||||||
|
|
||||||
fast-string-width@3.0.2:
|
fast-string-width@3.0.2:
|
||||||
@@ -7105,6 +7212,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.5
|
'@types/hast': 3.0.5
|
||||||
|
|
||||||
|
help-me@5.0.0: {}
|
||||||
|
|
||||||
hermes-estree@0.25.1: {}
|
hermes-estree@0.25.1: {}
|
||||||
|
|
||||||
hermes-parser@0.25.1:
|
hermes-parser@0.25.1:
|
||||||
@@ -7350,6 +7459,8 @@ snapshots:
|
|||||||
|
|
||||||
jose@6.2.8: {}
|
jose@6.2.8: {}
|
||||||
|
|
||||||
|
joycon@3.1.1: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-yaml@4.3.1:
|
js-yaml@4.3.1:
|
||||||
@@ -7986,6 +8097,8 @@ snapshots:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.1.2
|
es-object-atoms: 1.1.2
|
||||||
|
|
||||||
|
on-exit-leak-free@2.1.2: {}
|
||||||
|
|
||||||
on-finished@2.4.1:
|
on-finished@2.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
ee-first: 1.1.1
|
ee-first: 1.1.1
|
||||||
@@ -8108,6 +8221,42 @@ snapshots:
|
|||||||
|
|
||||||
picomatch@4.0.5: {}
|
picomatch@4.0.5: {}
|
||||||
|
|
||||||
|
pino-abstract-transport@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
split2: 4.2.0
|
||||||
|
|
||||||
|
pino-pretty@13.1.3:
|
||||||
|
dependencies:
|
||||||
|
colorette: 2.0.20
|
||||||
|
dateformat: 4.6.3
|
||||||
|
fast-copy: 4.0.4
|
||||||
|
fast-safe-stringify: 2.1.1
|
||||||
|
help-me: 5.0.0
|
||||||
|
joycon: 3.1.1
|
||||||
|
minimist: 1.2.8
|
||||||
|
on-exit-leak-free: 2.1.2
|
||||||
|
pino-abstract-transport: 3.0.0
|
||||||
|
pump: 3.0.4
|
||||||
|
secure-json-parse: 4.1.0
|
||||||
|
sonic-boom: 4.2.1
|
||||||
|
strip-json-comments: 5.0.3
|
||||||
|
|
||||||
|
pino-std-serializers@7.1.0: {}
|
||||||
|
|
||||||
|
pino@10.3.1:
|
||||||
|
dependencies:
|
||||||
|
'@pinojs/redact': 0.4.0
|
||||||
|
atomic-sleep: 1.0.0
|
||||||
|
on-exit-leak-free: 2.1.2
|
||||||
|
pino-abstract-transport: 3.0.0
|
||||||
|
pino-std-serializers: 7.1.0
|
||||||
|
process-warning: 5.1.0
|
||||||
|
quick-format-unescaped: 4.0.4
|
||||||
|
real-require: 0.2.0
|
||||||
|
safe-stable-stringify: 2.5.0
|
||||||
|
sonic-boom: 4.2.1
|
||||||
|
thread-stream: 4.2.0
|
||||||
|
|
||||||
pkce-challenge@5.0.1: {}
|
pkce-challenge@5.0.1: {}
|
||||||
|
|
||||||
pkg-up@3.1.0:
|
pkg-up@3.1.0:
|
||||||
@@ -8154,6 +8303,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
parse-ms: 4.0.0
|
parse-ms: 4.0.0
|
||||||
|
|
||||||
|
process-warning@5.1.0: {}
|
||||||
|
|
||||||
prompts@2.4.2:
|
prompts@2.4.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
kleur: 3.0.3
|
kleur: 3.0.3
|
||||||
@@ -8172,6 +8323,11 @@ snapshots:
|
|||||||
forwarded: 0.2.0
|
forwarded: 0.2.0
|
||||||
ipaddr.js: 1.9.1
|
ipaddr.js: 1.9.1
|
||||||
|
|
||||||
|
pump@3.0.4:
|
||||||
|
dependencies:
|
||||||
|
end-of-stream: 1.4.5
|
||||||
|
once: 1.4.0
|
||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
qs@6.15.3:
|
qs@6.15.3:
|
||||||
@@ -8181,6 +8337,8 @@ snapshots:
|
|||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
|
quick-format-unescaped@4.0.4: {}
|
||||||
|
|
||||||
range-parser@1.3.0: {}
|
range-parser@1.3.0: {}
|
||||||
|
|
||||||
raw-body@3.0.2:
|
raw-body@3.0.2:
|
||||||
@@ -8248,6 +8406,10 @@ snapshots:
|
|||||||
|
|
||||||
react@19.2.8: {}
|
react@19.2.8: {}
|
||||||
|
|
||||||
|
real-require@0.2.0: {}
|
||||||
|
|
||||||
|
real-require@1.0.0: {}
|
||||||
|
|
||||||
recast@0.23.21:
|
recast@0.23.21:
|
||||||
dependencies:
|
dependencies:
|
||||||
ast-types: 0.16.1
|
ast-types: 0.16.1
|
||||||
@@ -8369,10 +8531,14 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
is-regex: 1.2.1
|
is-regex: 1.2.1
|
||||||
|
|
||||||
|
safe-stable-stringify@2.5.0: {}
|
||||||
|
|
||||||
safer-buffer@2.1.2: {}
|
safer-buffer@2.1.2: {}
|
||||||
|
|
||||||
scheduler@0.27.0: {}
|
scheduler@0.27.0: {}
|
||||||
|
|
||||||
|
secure-json-parse@4.1.0: {}
|
||||||
|
|
||||||
semver@6.3.1: {}
|
semver@6.3.1: {}
|
||||||
|
|
||||||
semver@7.8.5: {}
|
semver@7.8.5: {}
|
||||||
@@ -8548,6 +8714,10 @@ snapshots:
|
|||||||
ip-address: 10.5.0
|
ip-address: 10.5.0
|
||||||
smart-buffer: 4.2.0
|
smart-buffer: 4.2.0
|
||||||
|
|
||||||
|
sonic-boom@4.2.1:
|
||||||
|
dependencies:
|
||||||
|
atomic-sleep: 1.0.0
|
||||||
|
|
||||||
sonner@2.0.8(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
|
sonner@2.0.8(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
@@ -8566,6 +8736,8 @@ snapshots:
|
|||||||
|
|
||||||
space-separated-tokens@2.0.2: {}
|
space-separated-tokens@2.0.2: {}
|
||||||
|
|
||||||
|
split2@4.2.0: {}
|
||||||
|
|
||||||
stable-hash@0.0.5: {}
|
stable-hash@0.0.5: {}
|
||||||
|
|
||||||
statuses@2.0.2: {}
|
statuses@2.0.2: {}
|
||||||
@@ -8661,6 +8833,8 @@ snapshots:
|
|||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
||||||
|
strip-json-comments@5.0.3: {}
|
||||||
|
|
||||||
style-to-js@1.1.21:
|
style-to-js@1.1.21:
|
||||||
dependencies:
|
dependencies:
|
||||||
style-to-object: 1.0.14
|
style-to-object: 1.0.14
|
||||||
@@ -8690,6 +8864,10 @@ snapshots:
|
|||||||
|
|
||||||
tapable@2.3.3: {}
|
tapable@2.3.3: {}
|
||||||
|
|
||||||
|
thread-stream@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
real-require: 1.0.0
|
||||||
|
|
||||||
tiny-invariant@1.3.3: {}
|
tiny-invariant@1.3.3: {}
|
||||||
|
|
||||||
tinyglobby@0.2.17:
|
tinyglobby@0.2.17:
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import "./src/bootstrap/async-local-storage-polyfill";
|
import "./src/bootstrap/async-local-storage-polyfill";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
import { createServer } from "node:http";
|
import { createServer } from "node:http";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import next from "next";
|
import next from "next";
|
||||||
|
import type { Level } from "pino";
|
||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import { getConfig } from "./src/lib/config/load";
|
import { getConfig } from "./src/lib/config/load";
|
||||||
import { migrateOnBoot } from "./src/lib/db/client";
|
import { migrateOnBoot } from "./src/lib/db/client";
|
||||||
@@ -10,6 +12,9 @@ import { syncAuthFromConfig } from "./src/lib/auth/sync";
|
|||||||
import { reconcileOrphanedRuns } from "./src/lib/runner/engine";
|
import { reconcileOrphanedRuns } from "./src/lib/runner/engine";
|
||||||
import { killAllRuns } from "./src/lib/runner/registry";
|
import { killAllRuns } from "./src/lib/runner/registry";
|
||||||
import { attachWsServer, authenticateUpgrade } from "./src/lib/ws/server";
|
import { attachWsServer, authenticateUpgrade } from "./src/lib/ws/server";
|
||||||
|
import { logger } from "./src/lib/logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "server" });
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV !== "production";
|
const dev = process.env.NODE_ENV !== "production";
|
||||||
const { config } = getConfig();
|
const { config } = getConfig();
|
||||||
@@ -31,11 +36,41 @@ process.env.TRIGGERSHELL_APP_ROOT = dir;
|
|||||||
const app = next({ dev, dir, hostname, port });
|
const app = next({ dev, dir, hostname, port });
|
||||||
const handle = app.getRequestHandler();
|
const handle = app.getRequestHandler();
|
||||||
|
|
||||||
|
// `/_next/*` asset requests happen dozens of times per page load and carry no operational
|
||||||
|
// signal - logged at debug so they don't drown out page/API requests in the default info level.
|
||||||
|
function accessLogLevel(pathname: string, statusCode: number): Level {
|
||||||
|
if (statusCode >= 500) return "error";
|
||||||
|
if (statusCode >= 400) return "warn";
|
||||||
|
return pathname.startsWith("/_next/") ? "debug" : "info";
|
||||||
|
}
|
||||||
|
|
||||||
app.prepare().then(() => {
|
app.prepare().then(() => {
|
||||||
const nextUpgradeHandler = app.getUpgradeHandler();
|
const nextUpgradeHandler = app.getUpgradeHandler();
|
||||||
|
|
||||||
const httpServer = createServer((req, res) => {
|
const httpServer = createServer((req, res) => {
|
||||||
handle(req, res);
|
const reqId = req.headers["x-request-id"]?.toString() ?? randomUUID();
|
||||||
|
req.headers["x-request-id"] = reqId;
|
||||||
|
const startedAt = process.hrtime.bigint();
|
||||||
|
|
||||||
|
res.on("finish", () => {
|
||||||
|
const durationMs = Number(process.hrtime.bigint() - startedAt) / 1e6;
|
||||||
|
const { pathname } = new URL(req.url ?? "/", "http://internal");
|
||||||
|
log[accessLogLevel(pathname, res.statusCode)](
|
||||||
|
{
|
||||||
|
reqId,
|
||||||
|
method: req.method,
|
||||||
|
path: pathname,
|
||||||
|
status: res.statusCode,
|
||||||
|
durationMs: Math.round(durationMs),
|
||||||
|
},
|
||||||
|
"request",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
handle(req, res).catch((error: unknown) => {
|
||||||
|
log.error({ reqId, err: error }, "unhandled error handling request");
|
||||||
|
if (!res.headersSent) res.writeHead(500).end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const wss = new WebSocketServer({ noServer: true });
|
const wss = new WebSocketServer({ noServer: true });
|
||||||
@@ -52,6 +87,7 @@ app.prepare().then(() => {
|
|||||||
authenticateUpgrade(req)
|
authenticateUpgrade(req)
|
||||||
.then((ok) => {
|
.then((ok) => {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
log.warn({ path: pathname }, "rejected unauthenticated WS upgrade");
|
||||||
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
@@ -60,21 +96,28 @@ app.prepare().then(() => {
|
|||||||
wss.emit("connection", ws, req);
|
wss.emit("connection", ws, req);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => socket.destroy());
|
.catch((error: unknown) => {
|
||||||
|
log.error({ err: error }, "error authenticating WS upgrade");
|
||||||
|
socket.destroy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
httpServer.listen(port, hostname, () => {
|
httpServer.listen(port, hostname, () => {
|
||||||
console.log(
|
log.info(
|
||||||
`> triggershell ready on http://${hostname}:${port} (${dev ? "development" : "production"})`,
|
{ hostname, port, mode: dev ? "development" : "production" },
|
||||||
|
"triggershell ready",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const shutdown = (signal: string) => {
|
const shutdown = (signal: string) => {
|
||||||
console.log(`> received ${signal}, shutting down...`);
|
log.info({ signal }, "shutting down");
|
||||||
killAllRuns();
|
killAllRuns();
|
||||||
httpServer.close(() => process.exit(0));
|
httpServer.close(() => process.exit(0));
|
||||||
// Force-exit if graceful shutdown hangs (e.g. a stuck WS connection).
|
// Force-exit if graceful shutdown hangs (e.g. a stuck WS connection).
|
||||||
setTimeout(() => process.exit(1), 5000).unref();
|
setTimeout(() => {
|
||||||
|
log.warn("graceful shutdown timed out, forcing exit");
|
||||||
|
process.exit(1);
|
||||||
|
}, 5000).unref();
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function DocPage({ params }: DocPageProps) {
|
|||||||
if (content === null) notFound();
|
if (content === null) notFound();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex max-w-3xl flex-col gap-6">
|
<div className="mx-auto flex max-w-5xl flex-col gap-6">
|
||||||
<Link
|
<Link
|
||||||
href="/docs"
|
href="/docs"
|
||||||
className="text-muted-foreground hover:text-foreground flex w-fit items-center gap-1 text-sm"
|
className="text-muted-foreground hover:text-foreground flex w-fit items-center gap-1 text-sm"
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import {
|
|||||||
recordFailedAttempt,
|
recordFailedAttempt,
|
||||||
clearAttempts,
|
clearAttempts,
|
||||||
} from "@/lib/auth/rate-limit";
|
} from "@/lib/auth/rate-limit";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "auth" });
|
||||||
|
|
||||||
const loginSchema = z.object({
|
const loginSchema = z.object({
|
||||||
username: z.string().min(1),
|
username: z.string().min(1),
|
||||||
@@ -20,6 +23,7 @@ const loginSchema = z.object({
|
|||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
const rateLimitKey = request.headers.get("x-forwarded-for") ?? "local";
|
const rateLimitKey = request.headers.get("x-forwarded-for") ?? "local";
|
||||||
if (isRateLimited(rateLimitKey)) {
|
if (isRateLimited(rateLimitKey)) {
|
||||||
|
log.warn({ from: rateLimitKey }, "login rate-limited");
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{ error: "Too many attempts, try again later." },
|
{ error: "Too many attempts, try again later." },
|
||||||
{ status: 429 },
|
{ status: 429 },
|
||||||
@@ -44,6 +48,10 @@ export async function POST(request: Request) {
|
|||||||
!(await verifyPassword(user.passwordHash, parsed.data.password))
|
!(await verifyPassword(user.passwordHash, parsed.data.password))
|
||||||
) {
|
) {
|
||||||
recordFailedAttempt(rateLimitKey);
|
recordFailedAttempt(rateLimitKey);
|
||||||
|
log.warn(
|
||||||
|
{ from: rateLimitKey, username: parsed.data.username },
|
||||||
|
"login failed: invalid credentials",
|
||||||
|
);
|
||||||
return Response.json({ error: "Invalid credentials" }, { status: 401 });
|
return Response.json({ error: "Invalid credentials" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,5 +66,7 @@ export async function POST(request: Request) {
|
|||||||
session.username = user.username;
|
session.username = user.username;
|
||||||
await session.save();
|
await session.save();
|
||||||
|
|
||||||
|
log.info({ from: rateLimitKey, username: user.username }, "login succeeded");
|
||||||
|
|
||||||
return Response.json({ user: { username: user.username } });
|
return Response.json({ user: { username: user.username } });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { requireAuth, unauthorizedResponse } from "@/lib/auth/guard";
|
|||||||
import { getDb } from "@/lib/db/client";
|
import { getDb } from "@/lib/db/client";
|
||||||
import { runs } from "@/lib/db/schema";
|
import { runs } from "@/lib/db/schema";
|
||||||
import { cancelRun } from "@/lib/runner/registry";
|
import { cancelRun } from "@/lib/runner/registry";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "api" });
|
||||||
|
|
||||||
export async function POST(
|
export async function POST(
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -27,11 +30,16 @@ export async function POST(
|
|||||||
|
|
||||||
const cancelled = cancelRun(runId);
|
const cancelled = cancelRun(runId);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
|
log.warn(
|
||||||
|
{ runId },
|
||||||
|
"cancel requested for a run not tracked by this process",
|
||||||
|
);
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{ error: "Run is not active in this server process" },
|
{ error: "Run is not active in this server process" },
|
||||||
{ status: 409 },
|
{ status: 409 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info({ runId, requestedBy: auth.identity }, "cancel requested via API");
|
||||||
return Response.json({ status: "cancelling" }, { status: 202 });
|
return Response.json({ status: "cancelling" }, { status: 202 });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import { requireAuth, unauthorizedResponse } from "@/lib/auth/guard";
|
|||||||
import { getScript } from "@/lib/config/load";
|
import { getScript } from "@/lib/config/load";
|
||||||
import { buildVariableSchema } from "@/lib/validation/variable-schema";
|
import { buildVariableSchema } from "@/lib/validation/variable-schema";
|
||||||
import { startRun } from "@/lib/runner/engine";
|
import { startRun } from "@/lib/runner/engine";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "api" });
|
||||||
|
|
||||||
export async function POST(
|
export async function POST(
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -26,11 +29,13 @@ export async function POST(
|
|||||||
const variableSchema = buildVariableSchema(script);
|
const variableSchema = buildVariableSchema(script);
|
||||||
const parsed = variableSchema.safeParse(variablesInput);
|
const parsed = variableSchema.safeParse(variablesInput);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
|
const fieldErrors = parsed.error.flatten().fieldErrors;
|
||||||
|
log.warn(
|
||||||
|
{ scriptId: script.id, fields: Object.keys(fieldErrors) },
|
||||||
|
"run request failed variable validation",
|
||||||
|
);
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{
|
{ error: "Validation failed", fieldErrors },
|
||||||
error: "Validation failed",
|
|
||||||
fieldErrors: parsed.error.flatten().fieldErrors,
|
|
||||||
},
|
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -2,12 +2,18 @@ import { eq } from "drizzle-orm";
|
|||||||
import { getDb } from "../db/client";
|
import { getDb } from "../db/client";
|
||||||
import { users, apiTokens } from "../db/schema";
|
import { users, apiTokens } from "../db/schema";
|
||||||
import { getConfig } from "../config/load";
|
import { getConfig } from "../config/load";
|
||||||
|
import { logger } from "../logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "auth" });
|
||||||
|
|
||||||
/** Config is the source of truth for who's allowed in; this materializes it into SQLite so the
|
/** Config is the source of truth for who's allowed in; this materializes it into SQLite so the
|
||||||
* runtime auth-check path is uniform and `lastLoginAt`/`lastUsedAt` can be tracked. Called on boot. */
|
* runtime auth-check path is uniform and `lastLoginAt`/`lastUsedAt` can be tracked. Called on boot. */
|
||||||
export function syncAuthFromConfig() {
|
export function syncAuthFromConfig() {
|
||||||
const { config } = getConfig();
|
const { config } = getConfig();
|
||||||
if (!config.auth.enabled) return;
|
if (!config.auth.enabled) {
|
||||||
|
log.debug("auth disabled, skipping config sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
|
|
||||||
@@ -53,4 +59,12 @@ export function syncAuthFromConfig() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
{
|
||||||
|
userCount: config.auth.users.length,
|
||||||
|
tokenCount: config.auth.tokens.length,
|
||||||
|
},
|
||||||
|
"synced auth config",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import fs from "node:fs";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { parse as parseYaml } from "yaml";
|
import { parse as parseYaml } from "yaml";
|
||||||
import { configSchema, type TriggerShellConfig } from "./schema";
|
import { configSchema, type TriggerShellConfig } from "./schema";
|
||||||
|
import { logger } from "../logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "config" });
|
||||||
|
|
||||||
export class ConfigError extends Error {
|
export class ConfigError extends Error {
|
||||||
issues: string[];
|
issues: string[];
|
||||||
@@ -90,6 +93,15 @@ declare global {
|
|||||||
export function getConfig(): LoadedConfig {
|
export function getConfig(): LoadedConfig {
|
||||||
if (!globalThis.__triggershellConfig) {
|
if (!globalThis.__triggershellConfig) {
|
||||||
globalThis.__triggershellConfig = loadConfig();
|
globalThis.__triggershellConfig = loadConfig();
|
||||||
|
const { config, configPath } = globalThis.__triggershellConfig;
|
||||||
|
log.info(
|
||||||
|
{
|
||||||
|
configPath,
|
||||||
|
scriptCount: config.scripts.length,
|
||||||
|
authEnabled: config.auth.enabled,
|
||||||
|
},
|
||||||
|
"config loaded",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return globalThis.__triggershellConfig;
|
return globalThis.__triggershellConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { drizzle } from "drizzle-orm/better-sqlite3";
|
|||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema";
|
||||||
import { getConfig } from "../config/load";
|
import { getConfig } from "../config/load";
|
||||||
|
import { logger } from "../logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "db" });
|
||||||
|
|
||||||
type Db = ReturnType<typeof drizzle<typeof schema>>;
|
type Db = ReturnType<typeof drizzle<typeof schema>>;
|
||||||
|
|
||||||
@@ -25,6 +28,7 @@ export function getDb(): Db {
|
|||||||
sqlite.pragma("journal_mode = WAL");
|
sqlite.pragma("journal_mode = WAL");
|
||||||
sqlite.pragma("foreign_keys = ON");
|
sqlite.pragma("foreign_keys = ON");
|
||||||
|
|
||||||
|
log.debug({ dbPath }, "opened database");
|
||||||
globalThis.__triggershellDb = drizzle(sqlite, { schema });
|
globalThis.__triggershellDb = drizzle(sqlite, { schema });
|
||||||
return globalThis.__triggershellDb;
|
return globalThis.__triggershellDb;
|
||||||
}
|
}
|
||||||
@@ -37,4 +41,5 @@ export function migrateOnBoot() {
|
|||||||
"../../../drizzle",
|
"../../../drizzle",
|
||||||
);
|
);
|
||||||
migrate(db, { migrationsFolder });
|
migrate(db, { migrationsFolder });
|
||||||
|
log.info("database migrations applied");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import pino from "pino";
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
|
const level = process.env.LOG_LEVEL ?? (isProduction ? "info" : "debug");
|
||||||
|
|
||||||
|
function createLogger(): pino.Logger {
|
||||||
|
return pino({
|
||||||
|
name: "triggershell",
|
||||||
|
level,
|
||||||
|
// Values are never logged under these key names in the first place - this is defense in
|
||||||
|
// depth against a future call site accidentally passing one through.
|
||||||
|
redact: {
|
||||||
|
paths: [
|
||||||
|
"password",
|
||||||
|
"*.password",
|
||||||
|
"token",
|
||||||
|
"*.token",
|
||||||
|
"passwordHash",
|
||||||
|
"*.passwordHash",
|
||||||
|
"tokenHash",
|
||||||
|
"*.tokenHash",
|
||||||
|
"sessionSecret",
|
||||||
|
"*.sessionSecret",
|
||||||
|
"authorization",
|
||||||
|
"*.authorization",
|
||||||
|
"cookie",
|
||||||
|
"*.cookie",
|
||||||
|
],
|
||||||
|
censor: "[REDACTED]",
|
||||||
|
},
|
||||||
|
transport: isProduction
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
target: "pino-pretty",
|
||||||
|
options: { colorize: true, ignore: "pid,hostname" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var __triggershellLogger: pino.Logger | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anchored on `globalThis` - see the comment in `runner/events.ts` for why: Next compiles Route
|
||||||
|
// Handlers through a separate module graph from what `server.ts` requires directly.
|
||||||
|
//
|
||||||
|
// Lazily constructed behind a Proxy rather than built at module-evaluation time: `config/load.ts`
|
||||||
|
// (imported by every CLI command for `loadConfig()`) also imports this module, and the dev-mode
|
||||||
|
// pretty transport spins up a worker thread the instant `pino({ transport })` runs. Without the
|
||||||
|
// laziness, a plain CLI invocation like `triggershell scripts list` would pay for a logger no
|
||||||
|
// backend code path in that process will ever call.
|
||||||
|
function ensureLogger(): pino.Logger {
|
||||||
|
if (!globalThis.__triggershellLogger) {
|
||||||
|
globalThis.__triggershellLogger = createLogger();
|
||||||
|
}
|
||||||
|
return globalThis.__triggershellLogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const logger: pino.Logger = new Proxy({} as pino.Logger, {
|
||||||
|
get(_target, prop) {
|
||||||
|
const target = ensureLogger();
|
||||||
|
const value = Reflect.get(target, prop, target);
|
||||||
|
return typeof value === "function" ? value.bind(target) : value;
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -9,6 +9,9 @@ import type { ScriptConfig } from "../config/schema";
|
|||||||
import { buildInvocation, type Invocation } from "./build-args";
|
import { buildInvocation, type Invocation } from "./build-args";
|
||||||
import { registerRun, unregisterRun } from "./registry";
|
import { registerRun, unregisterRun } from "./registry";
|
||||||
import { emitRunMessage } from "./events";
|
import { emitRunMessage } from "./events";
|
||||||
|
import { logger } from "../logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "runner" });
|
||||||
|
|
||||||
export class ScriptNotFoundError extends Error {}
|
export class ScriptNotFoundError extends Error {}
|
||||||
|
|
||||||
@@ -49,9 +52,14 @@ export async function startRun({
|
|||||||
})
|
})
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
log.info({ runId, scriptId: script.id, triggeredBy }, "run queued");
|
||||||
|
|
||||||
// Fire and forget - the caller gets the runId immediately, progress streams over WS/polling.
|
// Fire and forget - the caller gets the runId immediately, progress streams over WS/polling.
|
||||||
void executeRun(runId, script, invocation, logFilePath).catch((error) => {
|
void executeRun(runId, script, invocation, logFilePath).catch((error) => {
|
||||||
console.error(`[runner] unhandled error executing run ${runId}:`, error);
|
log.error(
|
||||||
|
{ runId, scriptId: script.id, err: error },
|
||||||
|
"unhandled error executing run",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return runId;
|
return runId;
|
||||||
@@ -103,6 +111,11 @@ async function executeRun(
|
|||||||
|
|
||||||
registerRun({ runId, scriptId: script.id, controller });
|
registerRun({ runId, scriptId: script.id, controller });
|
||||||
setStatus("running", { startedAt: new Date() });
|
setStatus("running", { startedAt: new Date() });
|
||||||
|
log.info(
|
||||||
|
{ runId, scriptId: script.id, command: script.command },
|
||||||
|
"run started",
|
||||||
|
);
|
||||||
|
const startedAt = process.hrtime.bigint();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const subprocess = execa(script.command, invocation.argv, {
|
const subprocess = execa(script.command, invocation.argv, {
|
||||||
@@ -145,11 +158,26 @@ async function executeRun(
|
|||||||
? (result.shortMessage ?? null)
|
? (result.shortMessage ?? null)
|
||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
|
const durationMs = Number(process.hrtime.bigint() - startedAt) / 1e6;
|
||||||
|
log[status === "succeeded" ? "info" : "warn"](
|
||||||
|
{
|
||||||
|
runId,
|
||||||
|
scriptId: script.id,
|
||||||
|
status,
|
||||||
|
exitCode: result.exitCode ?? null,
|
||||||
|
durationMs: Math.round(durationMs),
|
||||||
|
},
|
||||||
|
"run finished",
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatus("failed", {
|
setStatus("failed", {
|
||||||
endedAt: new Date(),
|
endedAt: new Date(),
|
||||||
errorMessage: (error as Error).message,
|
errorMessage: (error as Error).message,
|
||||||
});
|
});
|
||||||
|
log.error(
|
||||||
|
{ runId, scriptId: script.id, err: error },
|
||||||
|
"run failed to execute",
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
logStream.end();
|
logStream.end();
|
||||||
unregisterRun(runId);
|
unregisterRun(runId);
|
||||||
@@ -170,7 +198,8 @@ export function reconcileOrphanedRuns() {
|
|||||||
})
|
})
|
||||||
.where(eq(runs.status, "running"))
|
.where(eq(runs.status, "running"))
|
||||||
.run();
|
.run();
|
||||||
db.update(runs)
|
const queued = db
|
||||||
|
.update(runs)
|
||||||
.set({
|
.set({
|
||||||
status: "interrupted",
|
status: "interrupted",
|
||||||
endedAt: now,
|
endedAt: now,
|
||||||
@@ -178,5 +207,14 @@ export function reconcileOrphanedRuns() {
|
|||||||
})
|
})
|
||||||
.where(eq(runs.status, "queued"))
|
.where(eq(runs.status, "queued"))
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
const interruptedCount = orphaned.changes + queued.changes;
|
||||||
|
if (interruptedCount > 0) {
|
||||||
|
log.warn(
|
||||||
|
{ runningCount: orphaned.changes, queuedCount: queued.changes },
|
||||||
|
"marked orphaned runs as interrupted after restart",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return orphaned;
|
return orphaned;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-2
@@ -13,8 +13,11 @@ import { apiTokens, runs } from "../db/schema";
|
|||||||
import { runEvents } from "../runner/events";
|
import { runEvents } from "../runner/events";
|
||||||
import { cancelRun } from "../runner/registry";
|
import { cancelRun } from "../runner/registry";
|
||||||
import { readLogSince } from "../runner/log-file";
|
import { readLogSince } from "../runner/log-file";
|
||||||
|
import { logger } from "../logger";
|
||||||
import { isClientMessage, type ServerMessage } from "./protocol";
|
import { isClientMessage, type ServerMessage } from "./protocol";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "ws" });
|
||||||
|
|
||||||
const subscriptions = new Map<string, Set<WebSocket>>();
|
const subscriptions = new Map<string, Set<WebSocket>>();
|
||||||
|
|
||||||
/** A run can start and finish (emitting all its output+status over `runEvents`) before a
|
/** A run can start and finish (emitting all its output+status over `runEvents`) before a
|
||||||
@@ -104,28 +107,41 @@ export async function authenticateUpgrade(
|
|||||||
|
|
||||||
export function attachWsServer(wss: WebSocketServer) {
|
export function attachWsServer(wss: WebSocketServer) {
|
||||||
wss.on("connection", (ws: WebSocket) => {
|
wss.on("connection", (ws: WebSocket) => {
|
||||||
|
const connId = crypto.randomUUID();
|
||||||
|
log.debug({ connId }, "connection opened");
|
||||||
|
|
||||||
ws.on("message", (raw) => {
|
ws.on("message", (raw) => {
|
||||||
let parsed: unknown;
|
let parsed: unknown;
|
||||||
try {
|
try {
|
||||||
parsed = JSON.parse(raw.toString());
|
parsed = JSON.parse(raw.toString());
|
||||||
} catch {
|
} catch {
|
||||||
|
log.warn({ connId }, "dropped unparseable WS message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isClientMessage(parsed)) {
|
||||||
|
log.warn({ connId, parsed }, "dropped unrecognized WS message");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isClientMessage(parsed)) return;
|
|
||||||
|
|
||||||
switch (parsed.type) {
|
switch (parsed.type) {
|
||||||
case "subscribe":
|
case "subscribe":
|
||||||
|
log.debug({ connId, runId: parsed.runId }, "subscribe");
|
||||||
subscribe(parsed.runId, ws, parsed.afterBytes ?? 0);
|
subscribe(parsed.runId, ws, parsed.afterBytes ?? 0);
|
||||||
break;
|
break;
|
||||||
case "unsubscribe":
|
case "unsubscribe":
|
||||||
|
log.debug({ connId, runId: parsed.runId }, "unsubscribe");
|
||||||
unsubscribe(parsed.runId, ws);
|
unsubscribe(parsed.runId, ws);
|
||||||
break;
|
break;
|
||||||
case "cancel":
|
case "cancel":
|
||||||
|
log.info({ connId, runId: parsed.runId }, "cancel requested via WS");
|
||||||
cancelRun(parsed.runId);
|
cancelRun(parsed.runId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("close", () => unsubscribeAll(ws));
|
ws.on("close", () => {
|
||||||
|
log.debug({ connId }, "connection closed");
|
||||||
|
unsubscribeAll(ws);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { NextResponse } from "next/server";
|
|||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
import { getConfig } from "@/lib/config/load";
|
import { getConfig } from "@/lib/config/load";
|
||||||
import { verifySessionCookieValue } from "@/lib/auth/session";
|
import { verifySessionCookieValue } from "@/lib/auth/session";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
|
const log = logger.child({ mod: "proxy" });
|
||||||
|
|
||||||
const PUBLIC_PATHS = [
|
const PUBLIC_PATHS = [
|
||||||
"/login",
|
"/login",
|
||||||
@@ -39,8 +42,13 @@ export async function proxy(request: NextRequest) {
|
|||||||
|
|
||||||
if (!session?.userId) {
|
if (!session?.userId) {
|
||||||
if (isApiRoute) {
|
if (isApiRoute) {
|
||||||
|
log.warn({ path: pathname }, "blocked unauthenticated API request");
|
||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
log.debug(
|
||||||
|
{ path: pathname },
|
||||||
|
"redirected unauthenticated request to login",
|
||||||
|
);
|
||||||
const loginUrl = new URL("/login", request.url);
|
const loginUrl = new URL("/login", request.url);
|
||||||
loginUrl.searchParams.set("next", pathname);
|
loginUrl.searchParams.set("next", pathname);
|
||||||
return NextResponse.redirect(loginUrl);
|
return NextResponse.redirect(loginUrl);
|
||||||
|
|||||||
Reference in New Issue
Block a user