Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c37f296c98 | |||
| e7ddc840ba | |||
| fa321d4c78 | |||
| 3b5da63917 | |||
| a620b9fa98 | |||
| a6d2a86ab3 | |||
| 4b591cc529 | |||
| ea24ec2ed4 | |||
| b9f9444fb7 | |||
| 419f171f16 | |||
| fc8527d1d9 | |||
| 719349dfcc | |||
| 2762750922 | |||
| 9386906af5 | |||
| ca9eff7051 | |||
| 6bef1d0031 | |||
| a5af2ca963 | |||
| 7e2aa19f3b | |||
| a2235c5511 | |||
| b1fc21bd19 | |||
| 305e9d3933 | |||
| b1c0a96f18 | |||
| ea289a3b79 | |||
| 77f8afc857 | |||
| 4ba8e6bc1e | |||
| 64cbe74d35 | |||
| d045ae4018 | |||
| 0b15edbb56 | |||
| 8726487e22 | |||
| 79c7fc388f | |||
| e43f4c40d0 | |||
| 7396fddf1d | |||
| afe6f4df95 | |||
| a915345307 |
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
id: npm-ci-test
|
||||
run: npm run ci-test
|
||||
run: npm run test
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -83,8 +83,7 @@ jobs:
|
||||
run: echo "hello" > prompt.txt
|
||||
|
||||
- name: Create System Prompt File
|
||||
run:
|
||||
echo "You are a helpful AI assistant for testing." > system-prompt.txt
|
||||
run: echo "You are a helpful AI assistant for testing." > system-prompt.txt
|
||||
|
||||
- name: Test Local Action with Prompt File
|
||||
id: test-action-prompt-file
|
||||
|
||||
@@ -11,12 +11,11 @@ permissions:
|
||||
|
||||
jobs:
|
||||
update_tag:
|
||||
name:
|
||||
Update the major tag to include the ${{ github.event.release.tag_name }}
|
||||
name: Update the major tag to include the ${{ github.event.release.tag_name }}
|
||||
changes
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update the ${{ env.TAG_NAME }} tag
|
||||
uses: actions/publish-action@v0.2.2
|
||||
uses: actions/publish-action@v0.3.0
|
||||
with:
|
||||
source-tag: ${{ env.TAG_NAME }}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: "@rollup/plugin-json"
|
||||
version: 6.1.0
|
||||
type: npm
|
||||
summary: Convert .json files to ES6 modules
|
||||
homepage: https://github.com/rollup/plugins/tree/master/packages/json#readme
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
notices: []
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: "@rollup/pluginutils"
|
||||
version: 5.1.4
|
||||
type: npm
|
||||
summary: A set of utility functions commonly used by Rollup plugins
|
||||
homepage: https://github.com/rollup/plugins/tree/master/packages/pluginutils#readme
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
notices: []
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
name: "@rollup/rollup-linux-x64-musl"
|
||||
version: 4.43.0
|
||||
type: npm
|
||||
summary: Native bindings for Rollup
|
||||
homepage: https://rollupjs.org/
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: Auto-generated MIT license text
|
||||
text: |
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
notices: []
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: "@types/estree"
|
||||
version: 1.0.7
|
||||
type: npm
|
||||
summary: TypeScript definitions for estree
|
||||
homepage: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |2
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
notices: []
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: "@types/js-yaml"
|
||||
version: 4.0.9
|
||||
type: npm
|
||||
summary: TypeScript definitions for js-yaml
|
||||
homepage: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/js-yaml
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |2
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
notices: []
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: estree-walker
|
||||
version: 2.0.2
|
||||
type: npm
|
||||
summary: Traverse an ESTree-compliant AST
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |-
|
||||
Copyright (c) 2015-20 [these people](https://github.com/Rich-Harris/estree-walker/graphs/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
- sources: README.md
|
||||
text: MIT
|
||||
notices: []
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: picomatch
|
||||
version: 4.0.2
|
||||
type: npm
|
||||
summary: Blazing fast and accurate glob matcher written in JavaScript, with no dependencies
|
||||
and full support for standard and extended Bash glob features, including braces,
|
||||
extglobs, POSIX brackets, and regular expressions.
|
||||
homepage: https://github.com/micromatch/picomatch
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-present, Jon Schlinkert.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
- sources: README.md
|
||||
text: |-
|
||||
Copyright © 2017-present, [Jon Schlinkert](https://github.com/jonschlinkert).
|
||||
Released under the [MIT License](LICENSE).
|
||||
notices: []
|
||||
@@ -1,438 +0,0 @@
|
||||
---
|
||||
name: rollup
|
||||
version: 4.43.0
|
||||
type: npm
|
||||
summary: Next-generation ES module bundler
|
||||
homepage: https://rollupjs.org/
|
||||
license: other
|
||||
licenses:
|
||||
- sources: LICENSE.md
|
||||
text: "# Rollup core license\nRollup is released under the MIT license:\n\nThe MIT
|
||||
License (MIT)\n\nCopyright (c) 2017 [these people](https://github.com/rollup/rollup/graphs/contributors)\n\nPermission
|
||||
is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the \"Software\"), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||
to permit persons to whom the Software is furnished to do so, subject to the following
|
||||
conditions:\n\nThe above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE
|
||||
IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||
THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n# Licenses of bundled dependencies\nThe
|
||||
published Rollup artifact additionally contains code with the following licenses:\nMIT,
|
||||
ISC, 0BSD\n\n# Bundled dependencies:\n## @jridgewell/sourcemap-codec\nLicense:
|
||||
MIT\nBy: Rich Harris\nRepository: git+https://github.com/jridgewell/sourcemap-codec.git\n\n>
|
||||
The MIT License\n> \n> Copyright (c) 2015 Rich Harris\n> \n> Permission is hereby
|
||||
granted, free of charge, to any person obtaining a copy\n> of this software and
|
||||
associated documentation files (the \"Software\"), to deal\n> in the Software
|
||||
without restriction, including without limitation the rights\n> to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell\n> copies of the Software,
|
||||
and to permit persons to whom the Software is\n> furnished to do so, subject to
|
||||
the following conditions:\n> \n> The above copyright notice and this permission
|
||||
notice shall be included in\n> all copies or substantial portions of the Software.\n>
|
||||
\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n>
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n>
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
@rollup/pluginutils\nLicense: MIT\nBy: Rich Harris\nRepository: rollup/plugins\n\n>
|
||||
The MIT License (MIT)\n> \n> Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy\n>
|
||||
of this software and associated documentation files (the \"Software\"), to deal\n>
|
||||
in the Software without restriction, including without limitation the rights\n>
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n> copies
|
||||
of the Software, and to permit persons to whom the Software is\n> furnished to
|
||||
do so, subject to the following conditions:\n> \n> The above copyright notice
|
||||
and this permission notice shall be included in\n> all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n## anymatch\nLicense:
|
||||
ISC\nBy: Elan Shanker\nRepository: https://github.com/micromatch/anymatch\n\n>
|
||||
The ISC License\n> \n> Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com)\n>
|
||||
\n> Permission to use, copy, modify, and/or distribute this software for any\n>
|
||||
purpose with or without fee is hereby granted, provided that the above\n> copyright
|
||||
notice and this permission notice appear in all copies.\n> \n> THE SOFTWARE IS
|
||||
PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n> WITH REGARD TO THIS
|
||||
SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n> MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES\n> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN\n> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
OUT OF OR\n> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
binary-extensions\nLicense: MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/binary-extensions\n\n>
|
||||
MIT License\n> \n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
Copyright (c) Paul Miller (https://paulmillr.com)\n> \n> Permission is hereby
|
||||
granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the \"Software\"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
to whom the Software is furnished to do so, subject to the following conditions:\n>
|
||||
\n> The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED
|
||||
\"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
braces\nLicense: MIT\nBy: Jon Schlinkert, Brian Woodward, Elan Shanker, Eugene
|
||||
Sharygin, hemanth.hm\nRepository: micromatch/braces\n\n> The MIT License (MIT)\n>
|
||||
\n> Copyright (c) 2014-present, Jon Schlinkert.\n> \n> Permission is hereby granted,
|
||||
free of charge, to any person obtaining a copy\n> of this software and associated
|
||||
documentation files (the \"Software\"), to deal\n> in the Software without restriction,
|
||||
including without limitation the rights\n> to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell\n> copies of the Software, and to permit persons
|
||||
to whom the Software is\n> furnished to do so, subject to the following conditions:\n>
|
||||
\n> The above copyright notice and this permission notice shall be included in\n>
|
||||
all copies or substantial portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED
|
||||
\"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
builtin-modules\nLicense: MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/builtin-modules\n\n>
|
||||
MIT License\n> \n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the \"Software\"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:\n> \n> The above copyright notice and
|
||||
this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
chokidar\nLicense: MIT\nBy: Paul Miller, Elan Shanker\nRepository: git+https://github.com/paulmillr/chokidar.git\n\n>
|
||||
The MIT License (MIT)\n> \n> Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com),
|
||||
Elan Shanker\n> \n> Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy\n> of this software and associated documentation files (the “Software”),
|
||||
to deal\n> in the Software without restriction, including without limitation the
|
||||
rights\n> to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell\n> copies of the Software, and to permit persons to whom the Software is\n>
|
||||
furnished to do so, subject to the following conditions:\n> \n> The above copyright
|
||||
notice and this permission notice shall be included in\n> all copies or substantial
|
||||
portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY
|
||||
OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n## date-time\nLicense:
|
||||
MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/date-time\n\n> MIT License\n>
|
||||
\n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the \"Software\"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:\n> \n> The above copyright notice and
|
||||
this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
fill-range\nLicense: MIT\nBy: Jon Schlinkert, Edo Rivai, Paul Miller, Rouven Weßling\nRepository:
|
||||
jonschlinkert/fill-range\n\n> The MIT License (MIT)\n> \n> Copyright (c) 2014-present,
|
||||
Jon Schlinkert.\n> \n> Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy\n> of this software and associated documentation files (the \"Software\"),
|
||||
to deal\n> in the Software without restriction, including without limitation the
|
||||
rights\n> to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell\n> copies of the Software, and to permit persons to whom the Software is\n>
|
||||
furnished to do so, subject to the following conditions:\n> \n> The above copyright
|
||||
notice and this permission notice shall be included in\n> all copies or substantial
|
||||
portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY
|
||||
OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n## flru\nLicense:
|
||||
MIT\nBy: Luke Edwards\nRepository: lukeed/flru\n\n> MIT License\n> \n> Copyright
|
||||
(c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)\n> \n> Permission is
|
||||
hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the \"Software\"), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||
to permit persons to whom the Software is furnished to do so, subject to the following
|
||||
conditions:\n> \n> The above copyright notice and this permission notice shall
|
||||
be included in all copies or substantial portions of the Software.\n> \n> THE
|
||||
SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
glob-parent\nLicense: ISC\nBy: Gulp Team, Elan Shanker, Blaine Bublitz\nRepository:
|
||||
gulpjs/glob-parent\n\n> The ISC License\n> \n> Copyright (c) 2015, 2019 Elan Shanker\n>
|
||||
\n> Permission to use, copy, modify, and/or distribute this software for any\n>
|
||||
purpose with or without fee is hereby granted, provided that the above\n> copyright
|
||||
notice and this permission notice appear in all copies.\n> \n> THE SOFTWARE IS
|
||||
PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n> WITH REGARD TO THIS
|
||||
SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n> MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES\n> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN\n> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
OUT OF OR\n> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
is-binary-path\nLicense: MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/is-binary-path\n\n>
|
||||
MIT License\n> \n> Copyright (c) 2019 Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com),
|
||||
Paul Miller (https://paulmillr.com)\n> \n> Permission is hereby granted, free
|
||||
of charge, to any person obtaining a copy of this software and associated documentation
|
||||
files (the \"Software\"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so, subject to the following conditions:\n> \n>
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED
|
||||
\"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
||||
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
is-extglob\nLicense: MIT\nBy: Jon Schlinkert\nRepository: jonschlinkert/is-extglob\n\n>
|
||||
The MIT License (MIT)\n> \n> Copyright (c) 2014-2016, Jon Schlinkert\n> \n> Permission
|
||||
is hereby granted, free of charge, to any person obtaining a copy\n> of this software
|
||||
and associated documentation files (the \"Software\"), to deal\n> in the Software
|
||||
without restriction, including without limitation the rights\n> to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell\n> copies of the Software,
|
||||
and to permit persons to whom the Software is\n> furnished to do so, subject to
|
||||
the following conditions:\n> \n> The above copyright notice and this permission
|
||||
notice shall be included in\n> all copies or substantial portions of the Software.\n>
|
||||
\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n>
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n>
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
is-glob\nLicense: MIT\nBy: Jon Schlinkert, Brian Woodward, Daniel Perez\nRepository:
|
||||
micromatch/is-glob\n\n> The MIT License (MIT)\n> \n> Copyright (c) 2014-2017,
|
||||
Jon Schlinkert.\n> \n> Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy\n> of this software and associated documentation files (the \"Software\"),
|
||||
to deal\n> in the Software without restriction, including without limitation the
|
||||
rights\n> to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell\n> copies of the Software, and to permit persons to whom the Software is\n>
|
||||
furnished to do so, subject to the following conditions:\n> \n> The above copyright
|
||||
notice and this permission notice shall be included in\n> all copies or substantial
|
||||
portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY
|
||||
OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n## is-number\nLicense:
|
||||
MIT\nBy: Jon Schlinkert, Olsten Larck, Rouven Weßling\nRepository: jonschlinkert/is-number\n\n>
|
||||
The MIT License (MIT)\n> \n> Copyright (c) 2014-present, Jon Schlinkert.\n> \n>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy\n>
|
||||
of this software and associated documentation files (the \"Software\"), to deal\n>
|
||||
in the Software without restriction, including without limitation the rights\n>
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n> copies
|
||||
of the Software, and to permit persons to whom the Software is\n> furnished to
|
||||
do so, subject to the following conditions:\n> \n> The above copyright notice
|
||||
and this permission notice shall be included in\n> all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n## is-reference\nLicense:
|
||||
MIT\nBy: Rich Harris\nRepository: git+https://github.com/Rich-Harris/is-reference.git\n\n---------------------------------------\n\n##
|
||||
locate-character\nLicense: MIT\nBy: Rich Harris\nRepository: git+https://gitlab.com/Rich-Harris/locate-character.git\n\n---------------------------------------\n\n##
|
||||
magic-string\nLicense: MIT\nBy: Rich Harris\nRepository: https://github.com/rich-harris/magic-string\n\n>
|
||||
Copyright 2018 Rich Harris\n> \n> Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software and associated documentation files
|
||||
(the \"Software\"), to deal in the Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following conditions:\n> \n> The above copyright
|
||||
notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY
|
||||
OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.\n\n---------------------------------------\n\n## normalize-path\nLicense:
|
||||
MIT\nBy: Jon Schlinkert, Blaine Bublitz\nRepository: jonschlinkert/normalize-path\n\n>
|
||||
The MIT License (MIT)\n> \n> Copyright (c) 2014-2018, Jon Schlinkert.\n> \n> Permission
|
||||
is hereby granted, free of charge, to any person obtaining a copy\n> of this software
|
||||
and associated documentation files (the \"Software\"), to deal\n> in the Software
|
||||
without restriction, including without limitation the rights\n> to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell\n> copies of the Software,
|
||||
and to permit persons to whom the Software is\n> furnished to do so, subject to
|
||||
the following conditions:\n> \n> The above copyright notice and this permission
|
||||
notice shall be included in\n> all copies or substantial portions of the Software.\n>
|
||||
\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n>
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n>
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
parse-ms\nLicense: MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/parse-ms\n\n>
|
||||
MIT License\n> \n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the \"Software\"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:\n> \n> The above copyright notice and
|
||||
this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
picocolors\nLicense: ISC\nBy: Alexey Raspopov\nRepository: alexeyraspopov/picocolors\n\n>
|
||||
ISC License\n> \n> Copyright (c) 2021-2024 Oleksii Raspopov, Kostiantyn Denysov,
|
||||
Anton Verinov\n> \n> Permission to use, copy, modify, and/or distribute this software
|
||||
for any\n> purpose with or without fee is hereby granted, provided that the above\n>
|
||||
copyright notice and this permission notice appear in all copies.\n> \n> THE SOFTWARE
|
||||
IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n> WITH REGARD TO
|
||||
THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n> MERCHANTABILITY AND FITNESS.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n> ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n> WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
DATA OR PROFITS, WHETHER IN AN\n> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
ACTION, ARISING OUT OF\n> OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
SOFTWARE.\n\n---------------------------------------\n\n## picomatch\nLicense:
|
||||
MIT\nBy: Jon Schlinkert\nRepository: micromatch/picomatch\n\n> The MIT License
|
||||
(MIT)\n> \n> Copyright (c) 2017-present, Jon Schlinkert.\n> \n> Permission is
|
||||
hereby granted, free of charge, to any person obtaining a copy\n> of this software
|
||||
and associated documentation files (the \"Software\"), to deal\n> in the Software
|
||||
without restriction, including without limitation the rights\n> to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell\n> copies of the Software,
|
||||
and to permit persons to whom the Software is\n> furnished to do so, subject to
|
||||
the following conditions:\n> \n> The above copyright notice and this permission
|
||||
notice shall be included in\n> all copies or substantial portions of the Software.\n>
|
||||
\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n>
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n>
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
pretty-bytes\nLicense: MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/pretty-bytes\n\n>
|
||||
MIT License\n> \n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the \"Software\"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:\n> \n> The above copyright notice and
|
||||
this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
pretty-ms\nLicense: MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/pretty-ms\n\n>
|
||||
MIT License\n> \n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the \"Software\"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:\n> \n> The above copyright notice and
|
||||
this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
readdirp\nLicense: MIT\nBy: Thorsten Lorenz, Paul Miller\nRepository: git://github.com/paulmillr/readdirp.git\n\n>
|
||||
MIT License\n> \n> Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy\n>
|
||||
of this software and associated documentation files (the \"Software\"), to deal\n>
|
||||
in the Software without restriction, including without limitation the rights\n>
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n> copies
|
||||
of the Software, and to permit persons to whom the Software is\n> furnished to
|
||||
do so, subject to the following conditions:\n> \n> The above copyright notice
|
||||
and this permission notice shall be included in all\n> copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE\n> SOFTWARE.\n\n---------------------------------------\n\n## signal-exit\nLicense:
|
||||
ISC\nBy: Ben Coe\nRepository: https://github.com/tapjs/signal-exit.git\n\n> The
|
||||
ISC License\n> \n> Copyright (c) 2015-2023 Benjamin Coe, Isaac Z. Schlueter, and
|
||||
Contributors\n> \n> Permission to use, copy, modify, and/or distribute this software\n>
|
||||
for any purpose with or without fee is hereby granted, provided\n> that the above
|
||||
copyright notice and this permission notice\n> appear in all copies.\n> \n> THE
|
||||
SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n> WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES\n> OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE\n> LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES\n> OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS,\n> WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION,\n> ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.\n\n---------------------------------------\n\n## time-zone\nLicense:
|
||||
MIT\nBy: Sindre Sorhus\nRepository: sindresorhus/time-zone\n\n> MIT License\n>
|
||||
\n> Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n>
|
||||
\n> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the \"Software\"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:\n> \n> The above copyright notice and
|
||||
this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n---------------------------------------\n\n##
|
||||
to-regex-range\nLicense: MIT\nBy: Jon Schlinkert, Rouven Weßling\nRepository:
|
||||
micromatch/to-regex-range\n\n> The MIT License (MIT)\n> \n> Copyright (c) 2015-present,
|
||||
Jon Schlinkert.\n> \n> Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy\n> of this software and associated documentation files (the \"Software\"),
|
||||
to deal\n> in the Software without restriction, including without limitation the
|
||||
rights\n> to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell\n> copies of the Software, and to permit persons to whom the Software is\n>
|
||||
furnished to do so, subject to the following conditions:\n> \n> The above copyright
|
||||
notice and this permission notice shall be included in\n> all copies or substantial
|
||||
portions of the Software.\n> \n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY
|
||||
OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN\n> THE SOFTWARE.\n\n---------------------------------------\n\n## tslib\nLicense:
|
||||
0BSD\nBy: Microsoft Corp.\nRepository: https://github.com/Microsoft/tslib.git\n\n>
|
||||
Copyright (c) Microsoft Corporation.\n> \n> Permission to use, copy, modify, and/or
|
||||
distribute this software for any\n> purpose with or without fee is hereby granted.\n>
|
||||
\n> THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH\n> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n>
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n>
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n>
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n>
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n> PERFORMANCE
|
||||
OF THIS SOFTWARE.\n\n---------------------------------------\n\n## yargs-parser\nLicense:
|
||||
ISC\nBy: Ben Coe\nRepository: https://github.com/yargs/yargs-parser.git\n\n> Copyright
|
||||
(c) 2016, Contributors\n> \n> Permission to use, copy, modify, and/or distribute
|
||||
this software\n> for any purpose with or without fee is hereby granted, provided\n>
|
||||
that the above copyright notice and this permission notice\n> appear in all copies.\n>
|
||||
\n> THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n>
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES\n> OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE\n> LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES\n> OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS,\n> WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION,\n> ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.\n"
|
||||
- sources: README.md
|
||||
text: "[MIT](https://github.com/rollup/rollup/blob/master/LICENSE.md)"
|
||||
notices: []
|
||||
@@ -1,16 +0,0 @@
|
||||
# See: https://prettier.io/docs/en/configuration
|
||||
|
||||
printWidth: 80
|
||||
tabWidth: 2
|
||||
useTabs: false
|
||||
semi: false
|
||||
singleQuote: true
|
||||
quoteProps: as-needed
|
||||
jsxSingleQuote: false
|
||||
trailingComma: none
|
||||
bracketSpacing: true
|
||||
bracketSameLine: true
|
||||
arrowParens: always
|
||||
proseWrap: always
|
||||
htmlWhitespaceSensitivity: css
|
||||
endOfLine: lf
|
||||
@@ -4,7 +4,6 @@
|
||||

|
||||
[](https://github.com/actions/typescript-action/actions/workflows/check-dist.yml)
|
||||
[](https://github.com/actions/typescript-action/actions/workflows/codeql-analysis.yml)
|
||||
[](./badges/coverage.svg)
|
||||
|
||||
Use AI models from [GitHub Models](https://github.com/marketplace/models) in
|
||||
your workflows.
|
||||
@@ -84,8 +83,7 @@ model: openai/gpt-4o
|
||||
```yaml
|
||||
messages:
|
||||
- role: system
|
||||
content:
|
||||
You are a helpful assistant that describes animals using JSON format
|
||||
content: You are a helpful assistant that describes animals using JSON format
|
||||
- role: user
|
||||
content: |-
|
||||
Describe a {{animal}}
|
||||
@@ -170,12 +168,24 @@ steps:
|
||||
token: ${{ secrets.USER_PAT }}
|
||||
```
|
||||
|
||||
If you want, you can use separate tokens for the AI inference endpoint
|
||||
and the GitHub MCP server:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: AI Inference with Separate MCP Token
|
||||
id: inference
|
||||
uses: actions/ai-inference@v1.2
|
||||
with:
|
||||
prompt: 'List my open pull requests and create a summary'
|
||||
enable-github-mcp: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
github-mcp-token: ${{ secrets.USER_PAT }}
|
||||
```
|
||||
|
||||
When MCP is enabled, the AI model will have access to GitHub tools and can
|
||||
perform actions like searching issues and PRs.
|
||||
|
||||
**Note:** For now, MCP integration cannot be used with the built-in token. You
|
||||
must pass a GitHub PAT into `token:` instead.
|
||||
|
||||
## Inputs
|
||||
|
||||
Various inputs are defined in [`action.yml`](action.yml) to let you configure
|
||||
@@ -193,6 +203,7 @@ the action:
|
||||
| `endpoint` | The endpoint to use for inference. If you're running this as part of an org, you should probably use the org-specific Models endpoint | `https://models.github.ai/inference` |
|
||||
| `max-tokens` | The max number of tokens to generate | 200 |
|
||||
| `enable-github-mcp` | Enable Model Context Protocol integration with GitHub tools | `false` |
|
||||
| `github-mcp-token` | Token to use for GitHub MCP server (defaults to the main token if not specified). Use a separate PAT for tighter security | `""` |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type * as core from '@actions/core'
|
||||
import { jest } from '@jest/globals'
|
||||
import {vi} from 'vitest'
|
||||
|
||||
export const debug = jest.fn<typeof core.debug>()
|
||||
export const error = jest.fn<typeof core.error>()
|
||||
export const info = jest.fn<typeof core.info>()
|
||||
export const getInput = jest.fn<typeof core.getInput>()
|
||||
export const getBooleanInput = jest.fn<typeof core.getBooleanInput>()
|
||||
export const setOutput = jest.fn<typeof core.setOutput>()
|
||||
export const setFailed = jest.fn<typeof core.setFailed>()
|
||||
export const warning = jest.fn<typeof core.warning>()
|
||||
export const debug = vi.fn<typeof core.debug>()
|
||||
export const error = vi.fn<typeof core.error>()
|
||||
export const info = vi.fn<typeof core.info>()
|
||||
export const getInput = vi.fn<typeof core.getInput>()
|
||||
export const getBooleanInput = vi.fn<typeof core.getBooleanInput>()
|
||||
export const setOutput = vi.fn<typeof core.setOutput>()
|
||||
export const setFailed = vi.fn<typeof core.setFailed>()
|
||||
export const warning = vi.fn<typeof core.warning>()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
messages:
|
||||
- role: system
|
||||
content:
|
||||
You are a helpful assistant that describes animals using JSON format
|
||||
content: You are a helpful assistant that describes animals using JSON format
|
||||
- role: user
|
||||
content: |-
|
||||
Describe a {{animal}}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { jest } from '@jest/globals'
|
||||
import {vi} from 'vitest'
|
||||
|
||||
export const wait = jest.fn<typeof import('../src/wait.js').wait>()
|
||||
export const wait = vi.fn<typeof import('../src/wait.js').wait>()
|
||||
|
||||
@@ -1,41 +1,37 @@
|
||||
import { describe, it, expect } from '@jest/globals'
|
||||
import {
|
||||
buildMessages,
|
||||
buildResponseFormat,
|
||||
buildInferenceRequest
|
||||
} from '../src/helpers'
|
||||
import { PromptConfig } from '../src/prompt'
|
||||
import {describe, it, expect} from 'vitest'
|
||||
import {buildMessages, buildResponseFormat, buildInferenceRequest} from '../src/helpers'
|
||||
import {PromptConfig} from '../src/prompt'
|
||||
|
||||
describe('helpers.ts - inference request building', () => {
|
||||
describe('buildMessages', () => {
|
||||
it('should build messages from prompt config', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
]
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
],
|
||||
}
|
||||
|
||||
const result = buildMessages(promptConfig)
|
||||
expect(result).toEqual([
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
])
|
||||
})
|
||||
|
||||
it('should build messages from legacy format', () => {
|
||||
const result = buildMessages(undefined, 'System prompt', 'User prompt')
|
||||
expect(result).toEqual([
|
||||
{ role: 'system', content: 'System prompt' },
|
||||
{ role: 'user', content: 'User prompt' }
|
||||
{role: 'system', content: 'System prompt'},
|
||||
{role: 'user', content: 'User prompt'},
|
||||
])
|
||||
})
|
||||
|
||||
it('should use default system prompt when none provided', () => {
|
||||
const result = buildMessages(undefined, undefined, 'User prompt')
|
||||
expect(result).toEqual([
|
||||
{ role: 'system', content: 'You are a helpful assistant' },
|
||||
{ role: 'user', content: 'User prompt' }
|
||||
{role: 'system', content: 'You are a helpful assistant'},
|
||||
{role: 'user', content: 'User prompt'},
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -47,8 +43,8 @@ describe('helpers.ts - inference request building', () => {
|
||||
responseFormat: 'json_schema',
|
||||
jsonSchema: JSON.stringify({
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
})
|
||||
schema: {type: 'object'},
|
||||
}),
|
||||
}
|
||||
|
||||
const result = buildResponseFormat(promptConfig)
|
||||
@@ -56,15 +52,15 @@ describe('helpers.ts - inference request building', () => {
|
||||
type: 'json_schema',
|
||||
json_schema: {
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
}
|
||||
schema: {type: 'object'},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should return undefined for text format', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [],
|
||||
responseFormat: 'text'
|
||||
responseFormat: 'text',
|
||||
}
|
||||
|
||||
const result = buildResponseFormat(promptConfig)
|
||||
@@ -73,7 +69,7 @@ describe('helpers.ts - inference request building', () => {
|
||||
|
||||
it('should return undefined when no response format specified', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: []
|
||||
messages: [],
|
||||
}
|
||||
|
||||
const result = buildResponseFormat(promptConfig)
|
||||
@@ -84,12 +80,10 @@ describe('helpers.ts - inference request building', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [],
|
||||
responseFormat: 'json_schema',
|
||||
jsonSchema: 'invalid json'
|
||||
jsonSchema: 'invalid json',
|
||||
}
|
||||
|
||||
expect(() => buildResponseFormat(promptConfig)).toThrow(
|
||||
'Invalid JSON schema'
|
||||
)
|
||||
expect(() => buildResponseFormat(promptConfig)).toThrow('Invalid JSON schema')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -97,14 +91,14 @@ describe('helpers.ts - inference request building', () => {
|
||||
it('should build complete inference request from prompt config', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
],
|
||||
responseFormat: 'json_schema',
|
||||
jsonSchema: JSON.stringify({
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
})
|
||||
schema: {type: 'object'},
|
||||
}),
|
||||
}
|
||||
|
||||
const result = buildInferenceRequest(
|
||||
@@ -114,13 +108,13 @@ describe('helpers.ts - inference request building', () => {
|
||||
'gpt-4',
|
||||
100,
|
||||
'https://api.test.com',
|
||||
'test-token'
|
||||
'test-token',
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
@@ -130,9 +124,9 @@ describe('helpers.ts - inference request building', () => {
|
||||
type: 'json_schema',
|
||||
json_schema: {
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
}
|
||||
}
|
||||
schema: {type: 'object'},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -144,19 +138,19 @@ describe('helpers.ts - inference request building', () => {
|
||||
'gpt-4',
|
||||
100,
|
||||
'https://api.test.com',
|
||||
'test-token'
|
||||
'test-token',
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
{ role: 'system', content: 'System prompt' },
|
||||
{ role: 'user', content: 'User prompt' }
|
||||
{role: 'system', content: 'System prompt'},
|
||||
{role: 'user', content: 'User prompt'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'test-token',
|
||||
responseFormat: undefined
|
||||
responseFormat: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+10
-23
@@ -1,26 +1,21 @@
|
||||
/**
|
||||
* Unit tests for the helpers module, src/helpers.ts
|
||||
*/
|
||||
import { jest } from '@jest/globals'
|
||||
import {vi, it, expect, beforeEach, describe} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Mock fs module
|
||||
const mockExistsSync = jest.fn()
|
||||
const mockReadFileSync = jest.fn()
|
||||
const mockExistsSync = vi.fn()
|
||||
const mockReadFileSync = vi.fn()
|
||||
|
||||
jest.unstable_mockModule('fs', () => ({
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: mockExistsSync,
|
||||
readFileSync: mockReadFileSync
|
||||
readFileSync: mockReadFileSync,
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// Import the module being tested
|
||||
const { loadContentFromFileOrInput } = await import('../src/helpers.js')
|
||||
const {loadContentFromFileOrInput} = await import('../src/helpers.js')
|
||||
|
||||
describe('helpers.ts', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('loadContentFromFileOrInput', () => {
|
||||
@@ -108,11 +103,7 @@ describe('helpers.ts', () => {
|
||||
|
||||
core.getInput.mockImplementation(() => '')
|
||||
|
||||
const result = loadContentFromFileOrInput(
|
||||
'file-input',
|
||||
'content-input',
|
||||
defaultValue
|
||||
)
|
||||
const result = loadContentFromFileOrInput('file-input', 'content-input', defaultValue)
|
||||
|
||||
expect(result).toBe(defaultValue)
|
||||
expect(mockExistsSync).not.toHaveBeenCalled()
|
||||
@@ -136,11 +127,7 @@ describe('helpers.ts', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
core.getInput.mockImplementation(() => undefined as any)
|
||||
|
||||
const result = loadContentFromFileOrInput(
|
||||
'file-input',
|
||||
'content-input',
|
||||
defaultValue
|
||||
)
|
||||
const result = loadContentFromFileOrInput('file-input', 'content-input', defaultValue)
|
||||
|
||||
expect(result).toBe(defaultValue)
|
||||
})
|
||||
|
||||
+94
-118
@@ -1,50 +1,45 @@
|
||||
/**
|
||||
* Unit tests for the inference module, src/inference.ts
|
||||
*/
|
||||
import { jest } from '@jest/globals'
|
||||
import {vi, type MockedFunction, beforeEach, expect, describe, it} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Mock Azure AI Inference
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockPost = jest.fn() as jest.MockedFunction<any>
|
||||
const mockPath = jest.fn(() => ({ post: mockPost }))
|
||||
const mockClient = jest.fn(() => ({ path: mockPath }))
|
||||
const mockPost = vi.fn() as MockedFunction<any>
|
||||
const mockPath = vi.fn(() => ({post: mockPost}))
|
||||
const mockClient = vi.fn(() => ({path: mockPath}))
|
||||
|
||||
jest.unstable_mockModule('@azure-rest/ai-inference', () => ({
|
||||
vi.mock('@azure-rest/ai-inference', () => ({
|
||||
default: mockClient,
|
||||
isUnexpected: jest.fn(() => false)
|
||||
isUnexpected: vi.fn(() => false),
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@azure/core-auth', () => ({
|
||||
AzureKeyCredential: jest.fn()
|
||||
vi.mock('@azure/core-auth', () => ({
|
||||
AzureKeyCredential: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock MCP functions
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockExecuteToolCalls = jest.fn() as jest.MockedFunction<any>
|
||||
jest.unstable_mockModule('../src/mcp.js', () => ({
|
||||
executeToolCalls: mockExecuteToolCalls
|
||||
const mockExecuteToolCalls = vi.fn() as MockedFunction<any>
|
||||
vi.mock('../src/mcp.js', () => ({
|
||||
executeToolCalls: mockExecuteToolCalls,
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// Import the module being tested
|
||||
const { simpleInference, mcpInference } = await import('../src/inference.js')
|
||||
const {simpleInference, mcpInference} = await import('../src/inference.js')
|
||||
|
||||
describe('inference.ts', () => {
|
||||
const mockRequest = {
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a test assistant' },
|
||||
{ role: 'user', content: 'Hello, AI!' }
|
||||
{role: 'system', content: 'You are a test assistant'},
|
||||
{role: 'user', content: 'Hello, AI!'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'test-token'
|
||||
token: 'test-token',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('simpleInference', () => {
|
||||
@@ -54,11 +49,11 @@ describe('inference.ts', () => {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
content: 'Hello, user!',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -66,9 +61,7 @@ describe('inference.ts', () => {
|
||||
const result = await simpleInference(mockRequest)
|
||||
|
||||
expect(result).toBe('Hello, user!')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Running simple inference without tools'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Running simple inference without tools')
|
||||
expect(core.info).toHaveBeenCalledWith('Model response: Hello, user!')
|
||||
|
||||
// Verify the request structure
|
||||
@@ -77,16 +70,16 @@ describe('inference.ts', () => {
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You are a test assistant'
|
||||
content: 'You are a test assistant',
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, AI!'
|
||||
}
|
||||
content: 'Hello, AI!',
|
||||
},
|
||||
],
|
||||
max_tokens: 100,
|
||||
model: 'gpt-4'
|
||||
}
|
||||
model: 'gpt-4',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -96,11 +89,11 @@ describe('inference.ts', () => {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
content: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -108,9 +101,7 @@ describe('inference.ts', () => {
|
||||
const result = await simpleInference(mockRequest)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Model response: No response content'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Model response: No response content')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -124,10 +115,10 @@ describe('inference.ts', () => {
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
description: 'A test tool',
|
||||
parameters: { type: 'object' }
|
||||
}
|
||||
}
|
||||
]
|
||||
parameters: {type: 'object'},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
it('performs inference without tool calls', async () => {
|
||||
@@ -137,11 +128,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!',
|
||||
tool_calls: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -149,13 +140,9 @@ describe('inference.ts', () => {
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(result).toBe('Hello, user!')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Running GitHub MCP inference with tools'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Running GitHub MCP inference with tools')
|
||||
expect(core.info).toHaveBeenCalledWith('MCP inference iteration 1')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'No tool calls requested, ending GitHub MCP inference loop'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('No tool calls requested, ending GitHub MCP inference loop')
|
||||
|
||||
// The MCP inference loop will always add the assistant message, even when there are no tool calls
|
||||
// So we don't check the exact messages, just that tools were included
|
||||
@@ -173,9 +160,9 @@ describe('inference.ts', () => {
|
||||
id: 'call-123',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: '{"param": "value"}'
|
||||
}
|
||||
}
|
||||
arguments: '{"param": "value"}',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const toolResults = [
|
||||
@@ -183,8 +170,8 @@ describe('inference.ts', () => {
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: 'Tool result'
|
||||
}
|
||||
content: 'Tool result',
|
||||
},
|
||||
]
|
||||
|
||||
// First response with tool calls
|
||||
@@ -194,11 +181,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'I need to use a tool.',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Second response after tool execution
|
||||
@@ -208,26 +195,21 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Here is the final answer.',
|
||||
tool_calls: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost
|
||||
.mockResolvedValueOnce(firstResponse)
|
||||
.mockResolvedValueOnce(secondResponse)
|
||||
mockPost.mockResolvedValueOnce(firstResponse).mockResolvedValueOnce(secondResponse)
|
||||
|
||||
mockExecuteToolCalls.mockResolvedValue(toolResults)
|
||||
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(result).toBe('Here is the final answer.')
|
||||
expect(mockExecuteToolCalls).toHaveBeenCalledWith(
|
||||
mockMcpClient.client,
|
||||
toolCalls
|
||||
)
|
||||
expect(mockExecuteToolCalls).toHaveBeenCalledWith(mockMcpClient.client, toolCalls)
|
||||
expect(mockPost).toHaveBeenCalledTimes(2)
|
||||
|
||||
// Verify the second call includes the conversation history
|
||||
@@ -245,9 +227,9 @@ describe('inference.ts', () => {
|
||||
id: 'call-123',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: '{}'
|
||||
}
|
||||
}
|
||||
arguments: '{}',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const toolResults = [
|
||||
@@ -255,8 +237,8 @@ describe('inference.ts', () => {
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: 'Tool result'
|
||||
}
|
||||
content: 'Tool result',
|
||||
},
|
||||
]
|
||||
|
||||
// Always respond with tool calls to trigger infinite loop
|
||||
@@ -266,11 +248,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Using tool again.',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(responseWithToolCalls)
|
||||
@@ -279,9 +261,7 @@ describe('inference.ts', () => {
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledTimes(5) // Max iterations reached
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'GitHub MCP inference loop exceeded maximum iterations (5)'
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith('GitHub MCP inference loop exceeded maximum iterations (5)')
|
||||
expect(result).toBe('Using tool again.') // Last assistant message
|
||||
})
|
||||
|
||||
@@ -292,11 +272,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!',
|
||||
tool_calls: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -304,9 +284,7 @@ describe('inference.ts', () => {
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(result).toBe('Hello, user!')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'No tool calls requested, ending GitHub MCP inference loop'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('No tool calls requested, ending GitHub MCP inference loop')
|
||||
expect(mockExecuteToolCalls).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -314,8 +292,8 @@ describe('inference.ts', () => {
|
||||
const toolCalls = [
|
||||
{
|
||||
id: 'call-123',
|
||||
function: { name: 'test-tool', arguments: '{}' }
|
||||
}
|
||||
function: {name: 'test-tool', arguments: '{}'},
|
||||
},
|
||||
]
|
||||
|
||||
const firstResponse = {
|
||||
@@ -324,11 +302,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'First message',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const secondResponse = {
|
||||
@@ -337,24 +315,22 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Second message',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost
|
||||
.mockResolvedValueOnce(firstResponse)
|
||||
.mockResolvedValue(secondResponse)
|
||||
mockPost.mockResolvedValueOnce(firstResponse).mockResolvedValue(secondResponse)
|
||||
|
||||
mockExecuteToolCalls.mockResolvedValue([
|
||||
{
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: 'result'
|
||||
}
|
||||
content: 'result',
|
||||
},
|
||||
])
|
||||
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import { describe, it, expect, beforeEach, jest } from '@jest/globals'
|
||||
import {describe, it, expect, beforeEach, vi, type MockedFunction, type Mock} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Create fs mocks
|
||||
const mockExistsSync = jest.fn()
|
||||
const mockReadFileSync = jest.fn()
|
||||
const mockWriteFileSync = jest.fn()
|
||||
const mockExistsSync = vi.fn()
|
||||
const mockReadFileSync = vi.fn()
|
||||
const mockWriteFileSync = vi.fn()
|
||||
|
||||
// Create inference mocks
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockSimpleInference = jest.fn() as jest.MockedFunction<any>
|
||||
const mockMcpInference = jest.fn()
|
||||
const mockSimpleInference = vi.fn() as MockedFunction<any>
|
||||
const mockMcpInference = vi.fn()
|
||||
|
||||
// Create MCP mocks
|
||||
const mockConnectToGitHubMCP = jest.fn()
|
||||
const mockConnectToGitHubMCP = vi.fn()
|
||||
|
||||
// Mock fs module
|
||||
jest.unstable_mockModule('fs', () => ({
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: mockExistsSync,
|
||||
readFileSync: mockReadFileSync,
|
||||
writeFileSync: mockWriteFileSync
|
||||
writeFileSync: mockWriteFileSync,
|
||||
}))
|
||||
|
||||
// Mock the inference functions
|
||||
jest.unstable_mockModule('../src/inference.js', () => ({
|
||||
vi.mock('../src/inference.js', () => ({
|
||||
simpleInference: mockSimpleInference,
|
||||
mcpInference: mockMcpInference
|
||||
mcpInference: mockMcpInference,
|
||||
}))
|
||||
|
||||
// Mock the MCP connection
|
||||
jest.unstable_mockModule('../src/mcp.js', () => ({
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP
|
||||
vi.mock('../src/mcp.js', () => ({
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP,
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// The module being tested should be imported dynamically. This ensures that the
|
||||
// mocks are used in place of any actual dependencies.
|
||||
const { run } = await import('../src/main.js')
|
||||
const {run} = await import('../src/main.js')
|
||||
|
||||
describe('main.ts - prompt.yml integration', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Mock environment variables
|
||||
process.env['GITHUB_TOKEN'] = 'test-token'
|
||||
@@ -62,7 +62,7 @@ describe('main.ts - prompt.yml integration', () => {
|
||||
})
|
||||
|
||||
// Mock core.getBooleanInput
|
||||
const mockGetBooleanInput = core.getBooleanInput as jest.Mock
|
||||
const mockGetBooleanInput = core.getBooleanInput as Mock
|
||||
mockGetBooleanInput.mockReturnValue(false)
|
||||
|
||||
// Mock fs.readFileSync for prompt file
|
||||
@@ -111,29 +111,23 @@ model: openai/gpt-4o
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'Be as concise as possible'
|
||||
content: 'Be as concise as possible',
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Compare cats and dogs, please'
|
||||
}
|
||||
content: 'Compare cats and dogs, please',
|
||||
},
|
||||
],
|
||||
modelName: 'openai/gpt-4o',
|
||||
maxTokens: 200,
|
||||
endpoint: 'https://models.github.ai/inference',
|
||||
token: 'test-token'
|
||||
})
|
||||
token: 'test-token',
|
||||
}),
|
||||
)
|
||||
|
||||
// Verify outputs were set
|
||||
expect(core.setOutput).toHaveBeenCalledWith(
|
||||
'response',
|
||||
'Mocked AI response'
|
||||
)
|
||||
expect(core.setOutput).toHaveBeenCalledWith(
|
||||
'response-file',
|
||||
expect.any(String)
|
||||
)
|
||||
expect(core.setOutput).toHaveBeenCalledWith('response', 'Mocked AI response')
|
||||
expect(core.setOutput).toHaveBeenCalledWith('response-file', expect.any(String))
|
||||
})
|
||||
|
||||
it('should fall back to legacy format when not using prompt YAML', async () => {
|
||||
@@ -165,18 +159,18 @@ model: openai/gpt-4o
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You are helpful'
|
||||
content: 'You are helpful',
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, world!'
|
||||
}
|
||||
content: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
modelName: 'openai/gpt-4o',
|
||||
maxTokens: 200,
|
||||
endpoint: 'https://models.github.ai/inference',
|
||||
token: 'test-token'
|
||||
})
|
||||
token: 'test-token',
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
+56
-67
@@ -1,31 +1,21 @@
|
||||
/**
|
||||
* Unit tests for the action's main functionality, src/main.ts
|
||||
*/
|
||||
import { jest } from '@jest/globals'
|
||||
import {vi, describe, expect, it, beforeEach, type MockedFunction} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Default to throwing errors to catch unexpected calls
|
||||
const mockExistsSync = jest.fn().mockImplementation(() => {
|
||||
throw new Error(
|
||||
'Unexpected call to existsSync - test should override this implementation'
|
||||
)
|
||||
const mockExistsSync = vi.fn().mockImplementation(() => {
|
||||
throw new Error('Unexpected call to existsSync - test should override this implementation')
|
||||
})
|
||||
const mockReadFileSync = jest.fn().mockImplementation(() => {
|
||||
throw new Error(
|
||||
'Unexpected call to readFileSync - test should override this implementation'
|
||||
)
|
||||
const mockReadFileSync = vi.fn().mockImplementation(() => {
|
||||
throw new Error('Unexpected call to readFileSync - test should override this implementation')
|
||||
})
|
||||
const mockWriteFileSync = jest.fn()
|
||||
const mockWriteFileSync = vi.fn()
|
||||
|
||||
/**
|
||||
* Helper function to mock file system operations for one or more files
|
||||
* @param fileContents - Object mapping file paths to their contents
|
||||
* @param nonExistentFiles - Array of file paths that should be treated as non-existent
|
||||
*/
|
||||
function mockFileContent(
|
||||
fileContents: Record<string, string> = {},
|
||||
nonExistentFiles: string[] = []
|
||||
): void {
|
||||
function mockFileContent(fileContents: Record<string, string> = {}, nonExistentFiles: string[] = []): void {
|
||||
// Mock existsSync to return true for files that exist, false for those that don't
|
||||
mockExistsSync.mockImplementation((...args: unknown[]): boolean => {
|
||||
const [path] = args as [string]
|
||||
@@ -55,11 +45,11 @@ function mockInputs(inputs: Record<string, string> = {}): void {
|
||||
token: 'fake-token',
|
||||
model: 'gpt-4',
|
||||
'max-tokens': '100',
|
||||
endpoint: 'https://api.test.com'
|
||||
endpoint: 'https://api.test.com',
|
||||
}
|
||||
|
||||
// Combine defaults with user-provided inputs
|
||||
const allInputs: Record<string, string> = { ...defaultInputs, ...inputs }
|
||||
const allInputs: Record<string, string> = {...defaultInputs, ...inputs}
|
||||
|
||||
core.getInput.mockImplementation((name: string) => {
|
||||
return allInputs[name] || ''
|
||||
@@ -76,46 +66,48 @@ function mockInputs(inputs: Record<string, string> = {}): void {
|
||||
*/
|
||||
function verifyStandardResponse(): void {
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'response', 'Hello, user!')
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'response-file',
|
||||
expect.stringContaining('modelResponse.txt')
|
||||
)
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(2, 'response-file', expect.stringContaining('modelResponse.txt'))
|
||||
}
|
||||
|
||||
jest.unstable_mockModule('fs', () => ({
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: mockExistsSync,
|
||||
readFileSync: mockReadFileSync,
|
||||
writeFileSync: mockWriteFileSync
|
||||
writeFileSync: mockWriteFileSync,
|
||||
}))
|
||||
|
||||
// Mock MCP and inference modules
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockConnectToGitHubMCP = jest.fn() as jest.MockedFunction<any>
|
||||
const mockConnectToGitHubMCP = vi.fn() as MockedFunction<any>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockSimpleInference = jest.fn() as jest.MockedFunction<any>
|
||||
const mockSimpleInference = vi.fn() as MockedFunction<any>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockMcpInference = jest.fn() as jest.MockedFunction<any>
|
||||
const mockMcpInference = vi.fn() as MockedFunction<any>
|
||||
|
||||
jest.unstable_mockModule('../src/mcp.js', () => ({
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP
|
||||
vi.mock('../src/mcp.js', () => ({
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP,
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('../src/inference.js', () => ({
|
||||
vi.mock('../src/inference.js', () => ({
|
||||
simpleInference: mockSimpleInference,
|
||||
mcpInference: mockMcpInference
|
||||
mcpInference: mockMcpInference,
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// Mock process.exit to prevent it from actually exiting during tests
|
||||
const mockProcessExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
||||
throw new Error('process.exit called')
|
||||
})
|
||||
|
||||
// The module being tested should be imported dynamically. This ensures that the
|
||||
// mocks are used in place of any actual dependencies.
|
||||
const { run } = await import('../src/main.js')
|
||||
const {run} = await import('../src/main.js')
|
||||
|
||||
describe('main.ts', () => {
|
||||
// Reset all mocks before each test
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockProcessExit.mockClear()
|
||||
|
||||
// Remove any existing GITHUB_TOKEN
|
||||
delete process.env.GITHUB_TOKEN
|
||||
@@ -128,7 +120,7 @@ describe('main.ts', () => {
|
||||
it('Sets the response output', async () => {
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.'
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
})
|
||||
|
||||
await run()
|
||||
@@ -140,36 +132,35 @@ describe('main.ts', () => {
|
||||
it('Sets a failed status when no prompt is set', async () => {
|
||||
mockInputs({
|
||||
prompt: '',
|
||||
'prompt-file': ''
|
||||
'prompt-file': '',
|
||||
})
|
||||
|
||||
await run()
|
||||
// Expect the run function to throw due to process.exit being mocked
|
||||
await expect(run()).rejects.toThrow('process.exit called')
|
||||
|
||||
expect(core.setFailed).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'Neither prompt-file nor prompt was set'
|
||||
)
|
||||
expect(core.setFailed).toHaveBeenCalledWith('Neither prompt-file nor prompt was set')
|
||||
expect(mockProcessExit).toHaveBeenCalledWith(1)
|
||||
})
|
||||
|
||||
it('uses simple inference when MCP is disabled', async () => {
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
'enable-github-mcp': 'false'
|
||||
'enable-github-mcp': 'false',
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(mockSimpleInference).toHaveBeenCalledWith({
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a test assistant.' },
|
||||
{ role: 'user', content: 'Hello, AI!' }
|
||||
{role: 'system', content: 'You are a test assistant.'},
|
||||
{role: 'user', content: 'Hello, AI!'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'fake-token',
|
||||
responseFormat: undefined
|
||||
responseFormat: undefined,
|
||||
})
|
||||
expect(mockConnectToGitHubMCP).not.toHaveBeenCalled()
|
||||
expect(mockMcpInference).not.toHaveBeenCalled()
|
||||
@@ -180,13 +171,13 @@ describe('main.ts', () => {
|
||||
const mockMcpClient = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
client: {} as any,
|
||||
tools: [{ type: 'function', function: { name: 'test-tool' } }]
|
||||
tools: [{type: 'function', function: {name: 'test-tool'}}],
|
||||
}
|
||||
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
'enable-github-mcp': 'true'
|
||||
'enable-github-mcp': 'true',
|
||||
})
|
||||
|
||||
mockConnectToGitHubMCP.mockResolvedValue(mockMcpClient)
|
||||
@@ -197,12 +188,12 @@ describe('main.ts', () => {
|
||||
expect(mockMcpInference).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a test assistant.' },
|
||||
{ role: 'user', content: 'Hello, AI!' }
|
||||
{role: 'system', content: 'You are a test assistant.'},
|
||||
{role: 'user', content: 'Hello, AI!'},
|
||||
],
|
||||
token: 'fake-token'
|
||||
token: 'fake-token',
|
||||
}),
|
||||
mockMcpClient
|
||||
mockMcpClient,
|
||||
)
|
||||
expect(mockSimpleInference).not.toHaveBeenCalled()
|
||||
verifyStandardResponse()
|
||||
@@ -212,7 +203,7 @@ describe('main.ts', () => {
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
'enable-github-mcp': 'true'
|
||||
'enable-github-mcp': 'true',
|
||||
})
|
||||
|
||||
mockConnectToGitHubMCP.mockResolvedValue(null)
|
||||
@@ -222,9 +213,7 @@ describe('main.ts', () => {
|
||||
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token')
|
||||
expect(mockSimpleInference).toHaveBeenCalled()
|
||||
expect(mockMcpInference).not.toHaveBeenCalled()
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'MCP connection failed, falling back to simple inference'
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith('MCP connection failed, falling back to simple inference')
|
||||
verifyStandardResponse()
|
||||
})
|
||||
|
||||
@@ -236,27 +225,27 @@ describe('main.ts', () => {
|
||||
|
||||
mockFileContent({
|
||||
[promptFile]: promptContent,
|
||||
[systemPromptFile]: systemPromptContent
|
||||
[systemPromptFile]: systemPromptContent,
|
||||
})
|
||||
|
||||
mockInputs({
|
||||
'prompt-file': promptFile,
|
||||
'system-prompt-file': systemPromptFile,
|
||||
'enable-github-mcp': 'false'
|
||||
'enable-github-mcp': 'false',
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(mockSimpleInference).toHaveBeenCalledWith({
|
||||
messages: [
|
||||
{ role: 'system', content: systemPromptContent },
|
||||
{ role: 'user', content: promptContent }
|
||||
{role: 'system', content: systemPromptContent},
|
||||
{role: 'user', content: promptContent},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'fake-token',
|
||||
responseFormat: undefined
|
||||
responseFormat: undefined,
|
||||
})
|
||||
verifyStandardResponse()
|
||||
})
|
||||
@@ -267,13 +256,13 @@ describe('main.ts', () => {
|
||||
mockFileContent({}, [promptFile])
|
||||
|
||||
mockInputs({
|
||||
'prompt-file': promptFile
|
||||
'prompt-file': promptFile,
|
||||
})
|
||||
|
||||
await run()
|
||||
// Expect the run function to throw due to process.exit being mocked
|
||||
await expect(run()).rejects.toThrow('process.exit called')
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
`File for prompt-file was not found: ${promptFile}`
|
||||
)
|
||||
expect(core.setFailed).toHaveBeenCalledWith(`File for prompt-file was not found: ${promptFile}`)
|
||||
expect(mockProcessExit).toHaveBeenCalledWith(1)
|
||||
})
|
||||
})
|
||||
|
||||
+52
-82
@@ -1,45 +1,37 @@
|
||||
/**
|
||||
* Unit tests for the MCP module, src/mcp.ts
|
||||
*/
|
||||
import { jest } from '@jest/globals'
|
||||
import {vi, type MockedFunction, describe, it, expect, beforeEach} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Mock MCP SDK
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockConnect = jest.fn() as jest.MockedFunction<any>
|
||||
const mockConnect = vi.fn() as MockedFunction<any>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockListTools = jest.fn() as jest.MockedFunction<any>
|
||||
const mockListTools = vi.fn() as MockedFunction<any>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockCallTool = jest.fn() as jest.MockedFunction<any>
|
||||
const mockCallTool = vi.fn() as MockedFunction<any>
|
||||
|
||||
const mockClient = {
|
||||
connect: mockConnect,
|
||||
listTools: mockListTools,
|
||||
callTool: mockCallTool
|
||||
callTool: mockCallTool,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
|
||||
jest.unstable_mockModule('@modelcontextprotocol/sdk/client/index.js', () => ({
|
||||
Client: jest.fn(() => mockClient)
|
||||
vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({
|
||||
Client: vi.fn(() => mockClient),
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule(
|
||||
'@modelcontextprotocol/sdk/client/streamableHttp.js',
|
||||
() => ({
|
||||
StreamableHTTPClientTransport: jest.fn()
|
||||
})
|
||||
)
|
||||
vi.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({
|
||||
StreamableHTTPClientTransport: vi.fn(),
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// Import the module being tested
|
||||
const { connectToGitHubMCP, executeToolCall, executeToolCalls } = await import(
|
||||
'../src/mcp.js'
|
||||
)
|
||||
const {connectToGitHubMCP, executeToolCall, executeToolCalls} = await import('../src/mcp.js')
|
||||
|
||||
describe('mcp.ts', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('connectToGitHubMCP', () => {
|
||||
@@ -49,20 +41,20 @@ describe('mcp.ts', () => {
|
||||
{
|
||||
name: 'test-tool-1',
|
||||
description: 'Test tool 1',
|
||||
inputSchema: { type: 'object', properties: {} }
|
||||
inputSchema: {type: 'object', properties: {}},
|
||||
},
|
||||
{
|
||||
name: 'test-tool-2',
|
||||
description: 'Test tool 2',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { param: { type: 'string' } }
|
||||
}
|
||||
}
|
||||
properties: {param: {type: 'string'}},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({ tools: mockTools })
|
||||
mockListTools.mockResolvedValue({tools: mockTools})
|
||||
|
||||
const result = await connectToGitHubMCP(token)
|
||||
|
||||
@@ -74,21 +66,13 @@ describe('mcp.ts', () => {
|
||||
function: {
|
||||
name: 'test-tool-1',
|
||||
description: 'Test tool 1',
|
||||
parameters: { type: 'object', properties: {} }
|
||||
}
|
||||
parameters: {type: 'object', properties: {}},
|
||||
},
|
||||
})
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Connecting to GitHub MCP server...'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Successfully connected to GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Retrieved 2 tools from GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Mapped 2 GitHub MCP tools for Azure AI Inference'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Connecting to GitHub MCP server...')
|
||||
expect(core.info).toHaveBeenCalledWith('Successfully connected to GitHub MCP server')
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 2 tools from GitHub MCP server')
|
||||
expect(core.info).toHaveBeenCalledWith('Mapped 2 GitHub MCP tools for Azure AI Inference')
|
||||
})
|
||||
|
||||
it('returns null when connection fails', async () => {
|
||||
@@ -100,27 +84,21 @@ describe('mcp.ts', () => {
|
||||
const result = await connectToGitHubMCP(token)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'Failed to connect to GitHub MCP server: Error: Connection failed'
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith('Failed to connect to GitHub MCP server: Error: Connection failed')
|
||||
})
|
||||
|
||||
it('handles empty tools list', async () => {
|
||||
const token = 'test-token'
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({ tools: [] })
|
||||
mockListTools.mockResolvedValue({tools: []})
|
||||
|
||||
const result = await connectToGitHubMCP(token)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.tools).toHaveLength(0)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Retrieved 0 tools from GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Mapped 0 GitHub MCP tools for Azure AI Inference'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
|
||||
expect(core.info).toHaveBeenCalledWith('Mapped 0 GitHub MCP tools for Azure AI Inference')
|
||||
})
|
||||
|
||||
it('handles undefined tools list', async () => {
|
||||
@@ -133,9 +111,7 @@ describe('mcp.ts', () => {
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.tools).toHaveLength(0)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Retrieved 0 tools from GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -146,11 +122,11 @@ describe('mcp.ts', () => {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: '{"param": "value"}'
|
||||
}
|
||||
arguments: '{"param": "value"}',
|
||||
},
|
||||
}
|
||||
const toolResult = {
|
||||
content: [{ type: 'text', text: 'Tool execution result' }]
|
||||
content: [{type: 'text', text: 'Tool execution result'}],
|
||||
}
|
||||
|
||||
mockCallTool.mockResolvedValue(toolResult)
|
||||
@@ -159,20 +135,16 @@ describe('mcp.ts', () => {
|
||||
|
||||
expect(mockCallTool).toHaveBeenCalledWith({
|
||||
name: 'test-tool',
|
||||
arguments: { param: 'value' }
|
||||
arguments: {param: 'value'},
|
||||
})
|
||||
expect(result).toEqual({
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: JSON.stringify(toolResult.content)
|
||||
content: JSON.stringify(toolResult.content),
|
||||
})
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Executing GitHub MCP tool: test-tool with args: {"param": "value"}'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'GitHub MCP tool test-tool executed successfully'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Executing GitHub MCP tool: test-tool with args: {"param": "value"}')
|
||||
expect(core.info).toHaveBeenCalledWith('GitHub MCP tool test-tool executed successfully')
|
||||
})
|
||||
|
||||
it('handles tool execution errors gracefully', async () => {
|
||||
@@ -181,8 +153,8 @@ describe('mcp.ts', () => {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'failing-tool',
|
||||
arguments: '{"param": "value"}'
|
||||
}
|
||||
arguments: '{"param": "value"}',
|
||||
},
|
||||
}
|
||||
const toolError = new Error('Tool execution failed')
|
||||
|
||||
@@ -194,10 +166,10 @@ describe('mcp.ts', () => {
|
||||
tool_call_id: 'call-456',
|
||||
role: 'tool',
|
||||
name: 'failing-tool',
|
||||
content: 'Error: Error: Tool execution failed'
|
||||
content: 'Error: Error: Tool execution failed',
|
||||
})
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'Failed to execute GitHub MCP tool failing-tool: Error: Tool execution failed'
|
||||
'Failed to execute GitHub MCP tool failing-tool: Error: Tool execution failed',
|
||||
)
|
||||
})
|
||||
|
||||
@@ -207,8 +179,8 @@ describe('mcp.ts', () => {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: 'invalid-json'
|
||||
}
|
||||
arguments: 'invalid-json',
|
||||
},
|
||||
}
|
||||
|
||||
const result = await executeToolCall(mockClient, toolCall)
|
||||
@@ -217,9 +189,7 @@ describe('mcp.ts', () => {
|
||||
expect(result.role).toBe('tool')
|
||||
expect(result.name).toBe('test-tool')
|
||||
expect(result.content).toContain('Error:')
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Failed to execute GitHub MCP tool test-tool:')
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining('Failed to execute GitHub MCP tool test-tool:'))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -229,21 +199,21 @@ describe('mcp.ts', () => {
|
||||
{
|
||||
id: 'call-1',
|
||||
type: 'function',
|
||||
function: { name: 'tool-1', arguments: '{}' }
|
||||
function: {name: 'tool-1', arguments: '{}'},
|
||||
},
|
||||
{
|
||||
id: 'call-2',
|
||||
type: 'function',
|
||||
function: { name: 'tool-2', arguments: '{"param": "value"}' }
|
||||
}
|
||||
function: {name: 'tool-2', arguments: '{"param": "value"}'},
|
||||
},
|
||||
]
|
||||
|
||||
mockCallTool
|
||||
.mockResolvedValueOnce({
|
||||
content: [{ type: 'text', text: 'Result 1' }]
|
||||
content: [{type: 'text', text: 'Result 1'}],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
content: [{ type: 'text', text: 'Result 2' }]
|
||||
content: [{type: 'text', text: 'Result 2'}],
|
||||
})
|
||||
|
||||
const results = await executeToolCalls(mockClient, toolCalls)
|
||||
@@ -266,18 +236,18 @@ describe('mcp.ts', () => {
|
||||
{
|
||||
id: 'call-1',
|
||||
type: 'function',
|
||||
function: { name: 'tool-1', arguments: '{}' }
|
||||
function: {name: 'tool-1', arguments: '{}'},
|
||||
},
|
||||
{
|
||||
id: 'call-2',
|
||||
type: 'function',
|
||||
function: { name: 'tool-2', arguments: '{}' }
|
||||
}
|
||||
function: {name: 'tool-2', arguments: '{}'},
|
||||
},
|
||||
]
|
||||
|
||||
mockCallTool
|
||||
.mockResolvedValueOnce({
|
||||
content: [{ type: 'text', text: 'Result 1' }]
|
||||
content: [{type: 'text', text: 'Result 1'}],
|
||||
})
|
||||
.mockRejectedValueOnce(new Error('Tool 2 failed'))
|
||||
|
||||
|
||||
+13
-26
@@ -1,12 +1,7 @@
|
||||
import { describe, it, expect } from '@jest/globals'
|
||||
import {describe, it, expect} from 'vitest'
|
||||
import * as path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import {
|
||||
parseTemplateVariables,
|
||||
replaceTemplateVariables,
|
||||
loadPromptFile,
|
||||
isPromptYamlFile
|
||||
} from '../src/prompt'
|
||||
import {fileURLToPath} from 'url'
|
||||
import {parseTemplateVariables, replaceTemplateVariables, loadPromptFile, isPromptYamlFile} from '../src/prompt'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
@@ -19,7 +14,7 @@ a: hello
|
||||
b: world
|
||||
`
|
||||
const result = parseTemplateVariables(input)
|
||||
expect(result).toEqual({ a: 'hello', b: 'world' })
|
||||
expect(result).toEqual({a: 'hello', b: 'world'})
|
||||
})
|
||||
|
||||
it('should parse multiline variables', () => {
|
||||
@@ -49,14 +44,14 @@ var2: |
|
||||
describe('replaceTemplateVariables', () => {
|
||||
it('should replace simple variables', () => {
|
||||
const text = 'Hello {{name}}, welcome to {{place}}!'
|
||||
const variables = { name: 'John', place: 'GitHub' }
|
||||
const variables = {name: 'John', place: 'GitHub'}
|
||||
const result = replaceTemplateVariables(text, variables)
|
||||
expect(result).toBe('Hello John, welcome to GitHub!')
|
||||
})
|
||||
|
||||
it('should leave unreplaced variables as is', () => {
|
||||
const text = 'Hello {{name}}, welcome to {{unknown}}!'
|
||||
const variables = { name: 'John' }
|
||||
const variables = {name: 'John'}
|
||||
const result = replaceTemplateVariables(text, variables)
|
||||
expect(result).toBe('Hello John, welcome to {{unknown}}!')
|
||||
})
|
||||
@@ -90,31 +85,25 @@ var2: |
|
||||
|
||||
describe('loadPromptFile', () => {
|
||||
it('should load simple prompt file', () => {
|
||||
const filePath = path.join(
|
||||
__dirname,
|
||||
'../__fixtures__/prompts/simple.prompt.yml'
|
||||
)
|
||||
const variables = { a: 'cats', b: 'dogs' }
|
||||
const filePath = path.join(__dirname, '../__fixtures__/prompts/simple.prompt.yml')
|
||||
const variables = {a: 'cats', b: 'dogs'}
|
||||
const result = loadPromptFile(filePath, variables)
|
||||
|
||||
expect(result.messages).toHaveLength(2)
|
||||
expect(result.messages[0]).toEqual({
|
||||
role: 'system',
|
||||
content: 'Be as concise as possible'
|
||||
content: 'Be as concise as possible',
|
||||
})
|
||||
expect(result.messages[1]).toEqual({
|
||||
role: 'user',
|
||||
content: 'Compare cats and dogs, please'
|
||||
content: 'Compare cats and dogs, please',
|
||||
})
|
||||
expect(result.model).toBe('openai/gpt-4o')
|
||||
})
|
||||
|
||||
it('should load JSON schema prompt file', () => {
|
||||
const filePath = path.join(
|
||||
__dirname,
|
||||
'../__fixtures__/prompts/json-schema.prompt.yml'
|
||||
)
|
||||
const variables = { animal: 'dog' }
|
||||
const filePath = path.join(__dirname, '../__fixtures__/prompts/json-schema.prompt.yml')
|
||||
const variables = {animal: 'dog'}
|
||||
const result = loadPromptFile(filePath, variables)
|
||||
|
||||
expect(result.messages).toHaveLength(2)
|
||||
@@ -125,9 +114,7 @@ var2: |
|
||||
})
|
||||
|
||||
it('should throw error for non-existent file', () => {
|
||||
expect(() => loadPromptFile('non-existent.prompt.yml')).toThrow(
|
||||
'Prompt file not found'
|
||||
)
|
||||
expect(() => loadPromptFile('non-existent.prompt.yml')).toThrow('Prompt file not found')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+5
-2
@@ -14,8 +14,7 @@ inputs:
|
||||
required: false
|
||||
default: ''
|
||||
prompt-file:
|
||||
description:
|
||||
Path to a file containing the prompt (supports .txt and .prompt.yml
|
||||
description: Path to a file containing the prompt (supports .txt and .prompt.yml
|
||||
formats)
|
||||
required: false
|
||||
default: ''
|
||||
@@ -51,6 +50,10 @@ inputs:
|
||||
description: Enable Model Context Protocol integration with GitHub tools
|
||||
required: false
|
||||
default: 'false'
|
||||
github-mcp-token:
|
||||
description: The token to use for GitHub MCP server (defaults to GITHUB_TOKEN if not specified)
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
# Define your outputs here.
|
||||
outputs:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="Coverage: 84.21%"><title>Coverage: 84.21%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">84.21%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">84.21%</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
+32
-31
@@ -41878,14 +41878,14 @@ async function connectToGitHubMCP(token) {
|
||||
requestInit: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'X-MCP-Readonly': 'true'
|
||||
}
|
||||
}
|
||||
'X-MCP-Readonly': 'true',
|
||||
},
|
||||
},
|
||||
});
|
||||
const client = new Client({
|
||||
name: 'ai-inference-action',
|
||||
version: '1.0.0',
|
||||
transport
|
||||
transport,
|
||||
});
|
||||
try {
|
||||
await client.connect(transport);
|
||||
@@ -41898,13 +41898,13 @@ async function connectToGitHubMCP(token) {
|
||||
const toolsResponse = await client.listTools();
|
||||
coreExports.info(`Retrieved ${toolsResponse.tools?.length || 0} tools from GitHub MCP server`);
|
||||
// Map GitHub MCP tools → Azure AI Inference tool definitions
|
||||
const tools = (toolsResponse.tools || []).map((t) => ({
|
||||
const tools = (toolsResponse.tools || []).map(t => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.inputSchema
|
||||
}
|
||||
parameters: t.inputSchema,
|
||||
},
|
||||
}));
|
||||
coreExports.info(`Mapped ${tools.length} GitHub MCP tools for Azure AI Inference`);
|
||||
return { client, tools };
|
||||
@@ -41918,14 +41918,14 @@ async function executeToolCall(githubMcpClient, toolCall) {
|
||||
const args = JSON.parse(toolCall.function.arguments);
|
||||
const result = await githubMcpClient.callTool({
|
||||
name: toolCall.function.name,
|
||||
arguments: args
|
||||
arguments: args,
|
||||
});
|
||||
coreExports.info(`GitHub MCP tool ${toolCall.function.name} executed successfully`);
|
||||
return {
|
||||
tool_call_id: toolCall.id,
|
||||
role: 'tool',
|
||||
name: toolCall.function.name,
|
||||
content: JSON.stringify(result.content)
|
||||
content: JSON.stringify(result.content),
|
||||
};
|
||||
}
|
||||
catch (toolError) {
|
||||
@@ -41934,7 +41934,7 @@ async function executeToolCall(githubMcpClient, toolCall) {
|
||||
tool_call_id: toolCall.id,
|
||||
role: 'tool',
|
||||
name: toolCall.function.name,
|
||||
content: `Error: ${toolError}`
|
||||
content: `Error: ${toolError}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -49083,9 +49083,7 @@ function handleUnexpectedResponse(response) {
|
||||
}
|
||||
// Handle other error cases
|
||||
throw new Error(`AI service returned error response (status: ${response.status})${errorCodeMsg}: ` +
|
||||
(typeof response.body === 'string'
|
||||
? response.body
|
||||
: JSON.stringify(response.body)));
|
||||
(typeof response.body === 'string' ? response.body : JSON.stringify(response.body)));
|
||||
}
|
||||
/**
|
||||
* Build messages array from either prompt config or legacy format
|
||||
@@ -49093,9 +49091,9 @@ function handleUnexpectedResponse(response) {
|
||||
function buildMessages(promptConfig, systemPrompt, prompt) {
|
||||
if (promptConfig?.messages && promptConfig.messages.length > 0) {
|
||||
// Use new message format
|
||||
return promptConfig.messages.map((msg) => ({
|
||||
return promptConfig.messages.map(msg => ({
|
||||
role: msg.role,
|
||||
content: msg.content
|
||||
content: msg.content,
|
||||
}));
|
||||
}
|
||||
else {
|
||||
@@ -49103,9 +49101,9 @@ function buildMessages(promptConfig, systemPrompt, prompt) {
|
||||
return [
|
||||
{
|
||||
role: 'system',
|
||||
content: systemPrompt || 'You are a helpful assistant'
|
||||
content: systemPrompt || 'You are a helpful assistant',
|
||||
},
|
||||
{ role: 'user', content: prompt || '' }
|
||||
{ role: 'user', content: prompt || '' },
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -49113,13 +49111,12 @@ function buildMessages(promptConfig, systemPrompt, prompt) {
|
||||
* Build response format object for API from prompt config
|
||||
*/
|
||||
function buildResponseFormat(promptConfig) {
|
||||
if (promptConfig?.responseFormat === 'json_schema' &&
|
||||
promptConfig.jsonSchema) {
|
||||
if (promptConfig?.responseFormat === 'json_schema' && promptConfig.jsonSchema) {
|
||||
try {
|
||||
const schema = JSON.parse(promptConfig.jsonSchema);
|
||||
return {
|
||||
type: 'json_schema',
|
||||
json_schema: schema
|
||||
json_schema: schema,
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
@@ -49140,7 +49137,7 @@ function buildInferenceRequest(promptConfig, systemPrompt, prompt, modelName, ma
|
||||
maxTokens,
|
||||
endpoint,
|
||||
token,
|
||||
responseFormat
|
||||
responseFormat,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49150,19 +49147,19 @@ function buildInferenceRequest(promptConfig, systemPrompt, prompt, modelName, ma
|
||||
async function simpleInference(request) {
|
||||
coreExports.info('Running simple inference without tools');
|
||||
const client = createClient(request.endpoint, new AzureKeyCredential(request.token), {
|
||||
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }
|
||||
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' },
|
||||
});
|
||||
const requestBody = {
|
||||
messages: request.messages,
|
||||
max_tokens: request.maxTokens,
|
||||
model: request.modelName
|
||||
model: request.modelName,
|
||||
};
|
||||
// Add response format if specified
|
||||
if (request.responseFormat) {
|
||||
requestBody.response_format = request.responseFormat;
|
||||
}
|
||||
const response = await client.path('/chat/completions').post({
|
||||
body: requestBody
|
||||
body: requestBody,
|
||||
});
|
||||
if (isUnexpected(response)) {
|
||||
handleUnexpectedResponse(response);
|
||||
@@ -49177,7 +49174,7 @@ async function simpleInference(request) {
|
||||
async function mcpInference(request, githubMcpClient) {
|
||||
coreExports.info('Running GitHub MCP inference with tools');
|
||||
const client = createClient(request.endpoint, new AzureKeyCredential(request.token), {
|
||||
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }
|
||||
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' },
|
||||
});
|
||||
// Start with the pre-processed messages
|
||||
const messages = [...request.messages];
|
||||
@@ -49190,14 +49187,14 @@ async function mcpInference(request, githubMcpClient) {
|
||||
messages: messages,
|
||||
max_tokens: request.maxTokens,
|
||||
model: request.modelName,
|
||||
tools: githubMcpClient.tools
|
||||
tools: githubMcpClient.tools,
|
||||
};
|
||||
// Add response format if specified (only on first iteration to avoid conflicts)
|
||||
if (iterationCount === 1 && request.responseFormat) {
|
||||
requestBody.response_format = request.responseFormat;
|
||||
}
|
||||
const response = await client.path('/chat/completions').post({
|
||||
body: requestBody
|
||||
body: requestBody,
|
||||
});
|
||||
if (isUnexpected(response)) {
|
||||
handleUnexpectedResponse(response);
|
||||
@@ -49209,7 +49206,7 @@ async function mcpInference(request, githubMcpClient) {
|
||||
messages.push({
|
||||
role: 'assistant',
|
||||
content: modelResponse || '',
|
||||
...(toolCalls && { tool_calls: toolCalls })
|
||||
...(toolCalls && { tool_calls: toolCalls }),
|
||||
});
|
||||
if (!toolCalls || toolCalls.length === 0) {
|
||||
coreExports.info('No tool calls requested, ending GitHub MCP inference loop');
|
||||
@@ -49227,7 +49224,7 @@ async function mcpInference(request, githubMcpClient) {
|
||||
const lastAssistantMessage = messages
|
||||
.slice()
|
||||
.reverse()
|
||||
.find((msg) => msg.role === 'assistant');
|
||||
.find(msg => msg.role === 'assistant');
|
||||
return lastAssistantMessage?.content || null;
|
||||
}
|
||||
|
||||
@@ -52133,13 +52130,15 @@ async function run() {
|
||||
if (token === undefined) {
|
||||
throw new Error('GITHUB_TOKEN is not set');
|
||||
}
|
||||
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
|
||||
const githubMcpToken = coreExports.getInput('github-mcp-token') || token;
|
||||
const endpoint = coreExports.getInput('endpoint');
|
||||
// Build the inference request with pre-processed messages and response format
|
||||
const inferenceRequest = buildInferenceRequest(promptConfig, systemPrompt, prompt, modelName, maxTokens, endpoint, token);
|
||||
const enableMcp = coreExports.getBooleanInput('enable-github-mcp') || false;
|
||||
let modelResponse = null;
|
||||
if (enableMcp) {
|
||||
const mcpClient = await connectToGitHubMCP(inferenceRequest.token);
|
||||
const mcpClient = await connectToGitHubMCP(githubMcpToken);
|
||||
if (mcpClient) {
|
||||
modelResponse = await mcpInference(inferenceRequest, mcpClient);
|
||||
}
|
||||
@@ -52163,8 +52162,10 @@ async function run() {
|
||||
coreExports.setFailed(error.message);
|
||||
}
|
||||
else {
|
||||
coreExports.setFailed('An unexpected error occurred');
|
||||
coreExports.setFailed(`An unexpected error occurred: ${JSON.stringify(error, null, 2)}`);
|
||||
}
|
||||
// Force exit to prevent hanging on open connections
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
function tempDir() {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+15
-22
@@ -1,50 +1,43 @@
|
||||
// See: https://eslint.org/docs/latest/use/configure/configuration-files
|
||||
|
||||
import { fixupPluginRules } from '@eslint/compat'
|
||||
import { FlatCompat } from '@eslint/eslintrc'
|
||||
import {FlatCompat} from '@eslint/eslintrc'
|
||||
import js from '@eslint/js'
|
||||
import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
||||
import tsParser from '@typescript-eslint/parser'
|
||||
import _import from 'eslint-plugin-import'
|
||||
import jest from 'eslint-plugin-jest'
|
||||
import prettier from 'eslint-plugin-prettier'
|
||||
import globals from 'globals'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import {fileURLToPath} from 'node:url'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
allConfig: js.configs.all,
|
||||
})
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ['**/coverage', '**/dist', '**/linter', '**/node_modules']
|
||||
ignores: ['**/coverage', '**/dist', '**/linter', '**/node_modules'],
|
||||
},
|
||||
...compat.extends(
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:jest/recommended',
|
||||
'plugin:prettier/recommended'
|
||||
'plugin:prettier/recommended',
|
||||
),
|
||||
{
|
||||
plugins: {
|
||||
import: fixupPluginRules(_import),
|
||||
jest,
|
||||
prettier,
|
||||
'@typescript-eslint': typescriptEslint
|
||||
'@typescript-eslint': typescriptEslint,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
Atomics: 'readonly',
|
||||
SharedArrayBuffer: 'readonly'
|
||||
SharedArrayBuffer: 'readonly',
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
@@ -53,17 +46,17 @@ export default [
|
||||
|
||||
parserOptions: {
|
||||
project: ['tsconfig.eslint.json'],
|
||||
tsconfigRootDir: '.'
|
||||
}
|
||||
tsconfigRootDir: '.',
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
alwaysTryTypes: true,
|
||||
project: 'tsconfig.eslint.json'
|
||||
}
|
||||
}
|
||||
project: 'tsconfig.eslint.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
@@ -75,7 +68,7 @@ export default [
|
||||
'no-console': 'off',
|
||||
'no-shadow': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'prettier/prettier': 'error'
|
||||
}
|
||||
}
|
||||
'prettier/prettier': 'error',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// See: https://jestjs.io/docs/configuration
|
||||
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} **/
|
||||
export default {
|
||||
clearMocks: true,
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['./src/**'],
|
||||
coverageDirectory: './coverage',
|
||||
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
|
||||
coverageReporters: ['json-summary', 'text', 'lcov'],
|
||||
// Uncomment the below lines if you would like to enforce a coverage threshold
|
||||
// for your action. This will fail the build if the coverage is below the
|
||||
// specified thresholds.
|
||||
// coverageThreshold: {
|
||||
// global: {
|
||||
// branches: 100,
|
||||
// functions: 100,
|
||||
// lines: 100,
|
||||
// statements: 100
|
||||
// }
|
||||
// },
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
moduleFileExtensions: ['ts', 'js'],
|
||||
preset: 'ts-jest',
|
||||
reporters: ['default'],
|
||||
resolver: 'ts-jest-resolver',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
testPathIgnorePatterns: ['/dist/', '/node_modules/'],
|
||||
transform: {
|
||||
'^.+\\.ts$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: 'tsconfig.eslint.json',
|
||||
useESM: true
|
||||
}
|
||||
]
|
||||
},
|
||||
verbose: true
|
||||
}
|
||||
Generated
+602
-4899
File diff suppressed because it is too large
Load Diff
+11
-30
@@ -1,21 +1,7 @@
|
||||
{
|
||||
"name": "typescript-action",
|
||||
"description": "GitHub Actions TypeScript template",
|
||||
"name": "ai-inference",
|
||||
"version": "1.0.0",
|
||||
"author": "",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/actions/typescript-action",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/actions/typescript-action.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/actions/typescript-action/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"actions"
|
||||
],
|
||||
"exports": {
|
||||
".": "./dist/index.js"
|
||||
},
|
||||
@@ -24,23 +10,21 @@
|
||||
},
|
||||
"scripts": {
|
||||
"bundle": "npm run format:write && npm run package",
|
||||
"ci-test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
|
||||
"coverage": "npx make-coverage-badge --output-path ./badges/coverage.svg",
|
||||
"format:write": "npx prettier --write .",
|
||||
"format:check": "npx prettier --check .",
|
||||
"lint": "npx eslint .",
|
||||
"local-action": "npx @github/local-action . src/main.ts .env",
|
||||
"package": "npx rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
|
||||
"package:watch": "npm run package -- --watch",
|
||||
"test": "npx cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
|
||||
"all": "npm run format:write && npm run lint && npm run test && npm run coverage && npm run package"
|
||||
"test": "vitest --run",
|
||||
"test:watch": "vitest --watch",
|
||||
"all": "npm run format:write && npm run lint && npm run test && npm run package"
|
||||
},
|
||||
"license": "MIT",
|
||||
"prettier": "@github/prettier-config",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"@modelcontextprotocol/sdk": "^1.15.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"js-yaml": "^4.1.0",
|
||||
"pkce-challenge": "^5.0.0"
|
||||
},
|
||||
@@ -49,12 +33,13 @@
|
||||
"@azure/core-auth": "latest",
|
||||
"@azure/core-sse": "latest",
|
||||
"@eslint/compat": "^1.3.0",
|
||||
"@github/local-action": "^3.2.1",
|
||||
"@jest/globals": "^30.0.2",
|
||||
"@github/local-action": "^5.1.0",
|
||||
"@github/prettier-config": "^0.0.6",
|
||||
"@rollup/plugin-commonjs": "^28.0.5",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^22.15.31",
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
||||
"@typescript-eslint/parser": "^8.32.1",
|
||||
@@ -62,16 +47,12 @@
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-import-resolver-typescript": "^4.4.3",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jest": "^28.14.0",
|
||||
"eslint-plugin-prettier": "^5.4.1",
|
||||
"jest": "^29.7.0",
|
||||
"make-coverage-badge": "^1.2.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-eslint": "^16.4.2",
|
||||
"rollup": "^4.43.0",
|
||||
"ts-jest": "^29.4.0",
|
||||
"ts-jest-resolver": "^2.0.1",
|
||||
"typescript": "^5.8.3"
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-linux-x64-gnu": "*"
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
// See: https://rollupjs.org/introduction/
|
||||
import { builtinModules } from 'node:module'
|
||||
import {builtinModules} from 'node:module'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import nodeResolve from '@rollup/plugin-node-resolve'
|
||||
import typescript from '@rollup/plugin-typescript'
|
||||
@@ -11,7 +11,7 @@ const config = {
|
||||
esModule: true,
|
||||
file: 'dist/index.js',
|
||||
format: 'es',
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
},
|
||||
external: [...builtinModules, /^node:/],
|
||||
plugins: [
|
||||
@@ -19,13 +19,13 @@ const config = {
|
||||
nodeResolve({
|
||||
preferBuiltins: true,
|
||||
browser: false,
|
||||
exportConditions: ['node']
|
||||
exportConditions: ['node'],
|
||||
}),
|
||||
commonjs({
|
||||
include: /node_modules/
|
||||
include: /node_modules/,
|
||||
}),
|
||||
json()
|
||||
]
|
||||
json(),
|
||||
],
|
||||
}
|
||||
|
||||
export default config
|
||||
|
||||
+20
-33
@@ -1,8 +1,8 @@
|
||||
import * as core from '@actions/core'
|
||||
import { GetChatCompletionsDefaultResponse } from '@azure-rest/ai-inference'
|
||||
import {GetChatCompletionsDefaultResponse} from '@azure-rest/ai-inference'
|
||||
import * as fs from 'fs'
|
||||
import { PromptConfig } from './prompt.js'
|
||||
import { InferenceRequest } from './inference.js'
|
||||
import {PromptConfig} from './prompt.js'
|
||||
import {InferenceRequest} from './inference.js'
|
||||
|
||||
/**
|
||||
* Helper function to load content from a file or use fallback input
|
||||
@@ -11,11 +11,7 @@ import { InferenceRequest } from './inference.js'
|
||||
* @param defaultValue - Default value to use if neither file nor content is provided
|
||||
* @returns The loaded content
|
||||
*/
|
||||
export function loadContentFromFileOrInput(
|
||||
filePathInput: string,
|
||||
contentInput: string,
|
||||
defaultValue?: string
|
||||
): string {
|
||||
export function loadContentFromFileOrInput(filePathInput: string, contentInput: string, defaultValue?: string): string {
|
||||
const filePath = core.getInput(filePathInput)
|
||||
const contentString = core.getInput(contentInput)
|
||||
|
||||
@@ -38,9 +34,7 @@ export function loadContentFromFileOrInput(
|
||||
* @param response - The response object from the AI service
|
||||
* @throws Error with appropriate error message based on response content
|
||||
*/
|
||||
export function handleUnexpectedResponse(
|
||||
response: GetChatCompletionsDefaultResponse
|
||||
): never {
|
||||
export function handleUnexpectedResponse(response: GetChatCompletionsDefaultResponse): never {
|
||||
// Extract x-ms-error-code from headers if available
|
||||
const errorCode = response.headers['x-ms-error-code']
|
||||
const errorCodeMsg = errorCode ? ` (error code: ${errorCode})` : ''
|
||||
@@ -54,16 +48,14 @@ export function handleUnexpectedResponse(
|
||||
if (!response.body) {
|
||||
throw new Error(
|
||||
`Failed to get response from AI service (status: ${response.status})${errorCodeMsg}. ` +
|
||||
'Please check network connection and endpoint configuration.'
|
||||
'Please check network connection and endpoint configuration.',
|
||||
)
|
||||
}
|
||||
|
||||
// Handle other error cases
|
||||
throw new Error(
|
||||
`AI service returned error response (status: ${response.status})${errorCodeMsg}: ` +
|
||||
(typeof response.body === 'string'
|
||||
? response.body
|
||||
: JSON.stringify(response.body))
|
||||
(typeof response.body === 'string' ? response.body : JSON.stringify(response.body)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,22 +65,22 @@ export function handleUnexpectedResponse(
|
||||
export function buildMessages(
|
||||
promptConfig?: PromptConfig,
|
||||
systemPrompt?: string,
|
||||
prompt?: string
|
||||
): Array<{ role: string; content: string }> {
|
||||
prompt?: string,
|
||||
): Array<{role: string; content: string}> {
|
||||
if (promptConfig?.messages && promptConfig.messages.length > 0) {
|
||||
// Use new message format
|
||||
return promptConfig.messages.map((msg) => ({
|
||||
return promptConfig.messages.map(msg => ({
|
||||
role: msg.role,
|
||||
content: msg.content
|
||||
content: msg.content,
|
||||
}))
|
||||
} else {
|
||||
// Use legacy format
|
||||
return [
|
||||
{
|
||||
role: 'system',
|
||||
content: systemPrompt || 'You are a helpful assistant'
|
||||
content: systemPrompt || 'You are a helpful assistant',
|
||||
},
|
||||
{ role: 'user', content: prompt || '' }
|
||||
{role: 'user', content: prompt || ''},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -97,22 +89,17 @@ export function buildMessages(
|
||||
* Build response format object for API from prompt config
|
||||
*/
|
||||
export function buildResponseFormat(
|
||||
promptConfig?: PromptConfig
|
||||
): { type: 'json_schema'; json_schema: unknown } | undefined {
|
||||
if (
|
||||
promptConfig?.responseFormat === 'json_schema' &&
|
||||
promptConfig.jsonSchema
|
||||
) {
|
||||
promptConfig?: PromptConfig,
|
||||
): {type: 'json_schema'; json_schema: unknown} | undefined {
|
||||
if (promptConfig?.responseFormat === 'json_schema' && promptConfig.jsonSchema) {
|
||||
try {
|
||||
const schema = JSON.parse(promptConfig.jsonSchema)
|
||||
return {
|
||||
type: 'json_schema',
|
||||
json_schema: schema
|
||||
json_schema: schema,
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Invalid JSON schema: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
throw new Error(`Invalid JSON schema: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
@@ -128,7 +115,7 @@ export function buildInferenceRequest(
|
||||
modelName: string,
|
||||
maxTokens: number,
|
||||
endpoint: string,
|
||||
token: string
|
||||
token: string,
|
||||
): InferenceRequest {
|
||||
const messages = buildMessages(promptConfig, systemPrompt, prompt)
|
||||
const responseFormat = buildResponseFormat(promptConfig)
|
||||
@@ -139,6 +126,6 @@ export function buildInferenceRequest(
|
||||
maxTokens,
|
||||
endpoint,
|
||||
token,
|
||||
responseFormat
|
||||
responseFormat,
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* The entrypoint for the action. This file simply imports and runs the action's
|
||||
* main logic.
|
||||
*/
|
||||
import { run } from './main.js'
|
||||
import {run} from './main.js'
|
||||
|
||||
/* istanbul ignore next */
|
||||
run()
|
||||
|
||||
+23
-38
@@ -1,8 +1,8 @@
|
||||
import * as core from '@actions/core'
|
||||
import ModelClient, { isUnexpected } from '@azure-rest/ai-inference'
|
||||
import { AzureKeyCredential } from '@azure/core-auth'
|
||||
import { GitHubMCPClient, executeToolCalls, MCPTool, ToolCall } from './mcp.js'
|
||||
import { handleUnexpectedResponse } from './helpers.js'
|
||||
import ModelClient, {isUnexpected} from '@azure-rest/ai-inference'
|
||||
import {AzureKeyCredential} from '@azure/core-auth'
|
||||
import {GitHubMCPClient, executeToolCalls, MCPTool, ToolCall} from './mcp.js'
|
||||
import {handleUnexpectedResponse} from './helpers.js'
|
||||
|
||||
interface ChatMessage {
|
||||
role: string
|
||||
@@ -14,17 +14,17 @@ interface ChatCompletionsRequestBody {
|
||||
messages: ChatMessage[]
|
||||
max_tokens: number
|
||||
model: string
|
||||
response_format?: { type: 'json_schema'; json_schema: unknown }
|
||||
response_format?: {type: 'json_schema'; json_schema: unknown}
|
||||
tools?: MCPTool[]
|
||||
}
|
||||
|
||||
export interface InferenceRequest {
|
||||
messages: Array<{ role: string; content: string }>
|
||||
messages: Array<{role: string; content: string}>
|
||||
modelName: string
|
||||
maxTokens: number
|
||||
endpoint: string
|
||||
token: string
|
||||
responseFormat?: { type: 'json_schema'; json_schema: unknown } // Processed response format for the API
|
||||
responseFormat?: {type: 'json_schema'; json_schema: unknown} // Processed response format for the API
|
||||
}
|
||||
|
||||
export interface InferenceResponse {
|
||||
@@ -42,23 +42,17 @@ export interface InferenceResponse {
|
||||
/**
|
||||
* Simple one-shot inference without tools
|
||||
*/
|
||||
export async function simpleInference(
|
||||
request: InferenceRequest
|
||||
): Promise<string | null> {
|
||||
export async function simpleInference(request: InferenceRequest): Promise<string | null> {
|
||||
core.info('Running simple inference without tools')
|
||||
|
||||
const client = ModelClient(
|
||||
request.endpoint,
|
||||
new AzureKeyCredential(request.token),
|
||||
{
|
||||
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }
|
||||
}
|
||||
)
|
||||
const client = ModelClient(request.endpoint, new AzureKeyCredential(request.token), {
|
||||
userAgentOptions: {userAgentPrefix: 'github-actions-ai-inference'},
|
||||
})
|
||||
|
||||
const requestBody: ChatCompletionsRequestBody = {
|
||||
messages: request.messages,
|
||||
max_tokens: request.maxTokens,
|
||||
model: request.modelName
|
||||
model: request.modelName,
|
||||
}
|
||||
|
||||
// Add response format if specified
|
||||
@@ -67,7 +61,7 @@ export async function simpleInference(
|
||||
}
|
||||
|
||||
const response = await client.path('/chat/completions').post({
|
||||
body: requestBody
|
||||
body: requestBody,
|
||||
})
|
||||
|
||||
if (isUnexpected(response)) {
|
||||
@@ -85,17 +79,13 @@ export async function simpleInference(
|
||||
*/
|
||||
export async function mcpInference(
|
||||
request: InferenceRequest,
|
||||
githubMcpClient: GitHubMCPClient
|
||||
githubMcpClient: GitHubMCPClient,
|
||||
): Promise<string | null> {
|
||||
core.info('Running GitHub MCP inference with tools')
|
||||
|
||||
const client = ModelClient(
|
||||
request.endpoint,
|
||||
new AzureKeyCredential(request.token),
|
||||
{
|
||||
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }
|
||||
}
|
||||
)
|
||||
const client = ModelClient(request.endpoint, new AzureKeyCredential(request.token), {
|
||||
userAgentOptions: {userAgentPrefix: 'github-actions-ai-inference'},
|
||||
})
|
||||
|
||||
// Start with the pre-processed messages
|
||||
const messages: ChatMessage[] = [...request.messages]
|
||||
@@ -111,7 +101,7 @@ export async function mcpInference(
|
||||
messages: messages,
|
||||
max_tokens: request.maxTokens,
|
||||
model: request.modelName,
|
||||
tools: githubMcpClient.tools
|
||||
tools: githubMcpClient.tools,
|
||||
}
|
||||
|
||||
// Add response format if specified (only on first iteration to avoid conflicts)
|
||||
@@ -120,7 +110,7 @@ export async function mcpInference(
|
||||
}
|
||||
|
||||
const response = await client.path('/chat/completions').post({
|
||||
body: requestBody
|
||||
body: requestBody,
|
||||
})
|
||||
|
||||
if (isUnexpected(response)) {
|
||||
@@ -136,7 +126,7 @@ export async function mcpInference(
|
||||
messages.push({
|
||||
role: 'assistant',
|
||||
content: modelResponse || '',
|
||||
...(toolCalls && { tool_calls: toolCalls })
|
||||
...(toolCalls && {tool_calls: toolCalls}),
|
||||
})
|
||||
|
||||
if (!toolCalls || toolCalls.length === 0) {
|
||||
@@ -147,10 +137,7 @@ export async function mcpInference(
|
||||
core.info(`Model requested ${toolCalls.length} tool calls`)
|
||||
|
||||
// Execute all tool calls via GitHub MCP
|
||||
const toolResults = await executeToolCalls(
|
||||
githubMcpClient.client,
|
||||
toolCalls
|
||||
)
|
||||
const toolResults = await executeToolCalls(githubMcpClient.client, toolCalls)
|
||||
|
||||
// Add tool results to the conversation
|
||||
messages.push(...toolResults)
|
||||
@@ -158,15 +145,13 @@ export async function mcpInference(
|
||||
core.info('Tool results added, continuing conversation...')
|
||||
}
|
||||
|
||||
core.warning(
|
||||
`GitHub MCP inference loop exceeded maximum iterations (${maxIterations})`
|
||||
)
|
||||
core.warning(`GitHub MCP inference loop exceeded maximum iterations (${maxIterations})`)
|
||||
|
||||
// Return the last assistant message content
|
||||
const lastAssistantMessage = messages
|
||||
.slice()
|
||||
.reverse()
|
||||
.find((msg) => msg.role === 'assistant')
|
||||
.find(msg => msg.role === 'assistant')
|
||||
|
||||
return lastAssistantMessage?.content || null
|
||||
}
|
||||
|
||||
+14
-17
@@ -2,15 +2,10 @@ import * as core from '@actions/core'
|
||||
import * as fs from 'fs'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
import { connectToGitHubMCP } from './mcp.js'
|
||||
import { simpleInference, mcpInference } from './inference.js'
|
||||
import { loadContentFromFileOrInput, buildInferenceRequest } from './helpers.js'
|
||||
import {
|
||||
loadPromptFile,
|
||||
parseTemplateVariables,
|
||||
isPromptYamlFile,
|
||||
PromptConfig
|
||||
} from './prompt.js'
|
||||
import {connectToGitHubMCP} from './mcp.js'
|
||||
import {simpleInference, mcpInference} from './inference.js'
|
||||
import {loadContentFromFileOrInput, buildInferenceRequest} from './helpers.js'
|
||||
import {loadPromptFile, parseTemplateVariables, isPromptYamlFile, PromptConfig} from './prompt.js'
|
||||
|
||||
const RESPONSE_FILE = 'modelResponse.txt'
|
||||
|
||||
@@ -42,11 +37,7 @@ export async function run(): Promise<void> {
|
||||
core.info('Using legacy prompt format')
|
||||
|
||||
prompt = loadContentFromFileOrInput('prompt-file', 'prompt')
|
||||
systemPrompt = loadContentFromFileOrInput(
|
||||
'system-prompt-file',
|
||||
'system-prompt',
|
||||
'You are a helpful assistant'
|
||||
)
|
||||
systemPrompt = loadContentFromFileOrInput('system-prompt-file', 'system-prompt', 'You are a helpful assistant')
|
||||
}
|
||||
|
||||
// Get common parameters
|
||||
@@ -58,6 +49,9 @@ export async function run(): Promise<void> {
|
||||
throw new Error('GITHUB_TOKEN is not set')
|
||||
}
|
||||
|
||||
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
|
||||
const githubMcpToken = core.getInput('github-mcp-token') || token
|
||||
|
||||
const endpoint = core.getInput('endpoint')
|
||||
|
||||
// Build the inference request with pre-processed messages and response format
|
||||
@@ -68,7 +62,7 @@ export async function run(): Promise<void> {
|
||||
modelName,
|
||||
maxTokens,
|
||||
endpoint,
|
||||
token
|
||||
token,
|
||||
)
|
||||
|
||||
const enableMcp = core.getBooleanInput('enable-github-mcp') || false
|
||||
@@ -76,7 +70,7 @@ export async function run(): Promise<void> {
|
||||
let modelResponse: string | null = null
|
||||
|
||||
if (enableMcp) {
|
||||
const mcpClient = await connectToGitHubMCP(inferenceRequest.token)
|
||||
const mcpClient = await connectToGitHubMCP(githubMcpToken)
|
||||
|
||||
if (mcpClient) {
|
||||
modelResponse = await mcpInference(inferenceRequest, mcpClient)
|
||||
@@ -100,8 +94,11 @@ export async function run(): Promise<void> {
|
||||
if (error instanceof Error) {
|
||||
core.setFailed(error.message)
|
||||
} else {
|
||||
core.setFailed('An unexpected error occurred')
|
||||
core.setFailed(`An unexpected error occurred: ${JSON.stringify(error, null, 2)}`)
|
||||
}
|
||||
|
||||
// Force exit to prevent hanging on open connections
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-33
@@ -1,6 +1,6 @@
|
||||
import * as core from '@actions/core'
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||
import {Client} from '@modelcontextprotocol/sdk/client/index.js'
|
||||
import {StreamableHTTPClientTransport} from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||
|
||||
export interface ToolResult {
|
||||
tool_call_id: string
|
||||
@@ -35,9 +35,7 @@ export interface GitHubMCPClient {
|
||||
/**
|
||||
* Connect to the GitHub MCP server and retrieve available tools
|
||||
*/
|
||||
export async function connectToGitHubMCP(
|
||||
token: string
|
||||
): Promise<GitHubMCPClient | null> {
|
||||
export async function connectToGitHubMCP(token: string): Promise<GitHubMCPClient | null> {
|
||||
const githubMcpUrl = 'https://api.githubcopilot.com/mcp/'
|
||||
|
||||
core.info('Connecting to GitHub MCP server...')
|
||||
@@ -46,15 +44,15 @@ export async function connectToGitHubMCP(
|
||||
requestInit: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'X-MCP-Readonly': 'true'
|
||||
}
|
||||
}
|
||||
'X-MCP-Readonly': 'true',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const client = new Client({
|
||||
name: 'ai-inference-action',
|
||||
version: '1.0.0',
|
||||
transport
|
||||
transport,
|
||||
})
|
||||
|
||||
try {
|
||||
@@ -67,42 +65,35 @@ export async function connectToGitHubMCP(
|
||||
core.info('Successfully connected to GitHub MCP server')
|
||||
|
||||
const toolsResponse = await client.listTools()
|
||||
core.info(
|
||||
`Retrieved ${toolsResponse.tools?.length || 0} tools from GitHub MCP server`
|
||||
)
|
||||
core.info(`Retrieved ${toolsResponse.tools?.length || 0} tools from GitHub MCP server`)
|
||||
|
||||
// Map GitHub MCP tools → Azure AI Inference tool definitions
|
||||
const tools = (toolsResponse.tools || []).map((t) => ({
|
||||
const tools = (toolsResponse.tools || []).map(t => ({
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.inputSchema
|
||||
}
|
||||
parameters: t.inputSchema,
|
||||
},
|
||||
}))
|
||||
|
||||
core.info(`Mapped ${tools.length} GitHub MCP tools for Azure AI Inference`)
|
||||
|
||||
return { client, tools }
|
||||
return {client, tools}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single tool call via GitHub MCP
|
||||
*/
|
||||
export async function executeToolCall(
|
||||
githubMcpClient: Client,
|
||||
toolCall: ToolCall
|
||||
): Promise<ToolResult> {
|
||||
core.info(
|
||||
`Executing GitHub MCP tool: ${toolCall.function.name} with args: ${toolCall.function.arguments}`
|
||||
)
|
||||
export async function executeToolCall(githubMcpClient: Client, toolCall: ToolCall): Promise<ToolResult> {
|
||||
core.info(`Executing GitHub MCP tool: ${toolCall.function.name} with args: ${toolCall.function.arguments}`)
|
||||
|
||||
try {
|
||||
const args = JSON.parse(toolCall.function.arguments)
|
||||
|
||||
const result = await githubMcpClient.callTool({
|
||||
name: toolCall.function.name,
|
||||
arguments: args
|
||||
arguments: args,
|
||||
})
|
||||
|
||||
core.info(`GitHub MCP tool ${toolCall.function.name} executed successfully`)
|
||||
@@ -111,18 +102,16 @@ export async function executeToolCall(
|
||||
tool_call_id: toolCall.id,
|
||||
role: 'tool',
|
||||
name: toolCall.function.name,
|
||||
content: JSON.stringify(result.content)
|
||||
content: JSON.stringify(result.content),
|
||||
}
|
||||
} catch (toolError) {
|
||||
core.warning(
|
||||
`Failed to execute GitHub MCP tool ${toolCall.function.name}: ${toolError}`
|
||||
)
|
||||
core.warning(`Failed to execute GitHub MCP tool ${toolCall.function.name}: ${toolError}`)
|
||||
|
||||
return {
|
||||
tool_call_id: toolCall.id,
|
||||
role: 'tool',
|
||||
name: toolCall.function.name,
|
||||
content: `Error: ${toolError}`
|
||||
content: `Error: ${toolError}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,10 +119,7 @@ export async function executeToolCall(
|
||||
/**
|
||||
* Execute all tool calls from a response via GitHub MCP
|
||||
*/
|
||||
export async function executeToolCalls(
|
||||
githubMcpClient: Client,
|
||||
toolCalls: ToolCall[]
|
||||
): Promise<ToolResult[]> {
|
||||
export async function executeToolCalls(githubMcpClient: Client, toolCalls: ToolCall[]): Promise<ToolResult[]> {
|
||||
const toolResults: ToolResult[] = []
|
||||
|
||||
for (const toolCall of toolCalls) {
|
||||
|
||||
+7
-24
@@ -33,26 +33,19 @@ export function parseTemplateVariables(input: string): TemplateVariables {
|
||||
}
|
||||
return parsed
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to parse template variables: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
throw new Error(`Failed to parse template variables: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace template variables in text using {{variable}} syntax
|
||||
*/
|
||||
export function replaceTemplateVariables(
|
||||
text: string,
|
||||
variables: TemplateVariables
|
||||
): string {
|
||||
export function replaceTemplateVariables(text: string, variables: TemplateVariables): string {
|
||||
return text.replace(/\{\{([\w.-]+)\}\}/g, (match, variableName) => {
|
||||
if (variableName in variables) {
|
||||
return variables[variableName]
|
||||
}
|
||||
core.warning(
|
||||
`Template variable '${variableName}' not found in input variables`
|
||||
)
|
||||
core.warning(`Template variable '${variableName}' not found in input variables`)
|
||||
return match // Return the original placeholder if variable not found
|
||||
})
|
||||
}
|
||||
@@ -60,10 +53,7 @@ export function replaceTemplateVariables(
|
||||
/**
|
||||
* Load and parse a prompt YAML file with template variable substitution
|
||||
*/
|
||||
export function loadPromptFile(
|
||||
filePath: string,
|
||||
templateVariables: TemplateVariables = {}
|
||||
): PromptConfig {
|
||||
export function loadPromptFile(filePath: string, templateVariables: TemplateVariables = {}): PromptConfig {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`Prompt file not found: ${filePath}`)
|
||||
}
|
||||
@@ -71,10 +61,7 @@ export function loadPromptFile(
|
||||
const fileContent = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
// Apply template variable substitution
|
||||
const processedContent = replaceTemplateVariables(
|
||||
fileContent,
|
||||
templateVariables
|
||||
)
|
||||
const processedContent = replaceTemplateVariables(fileContent, templateVariables)
|
||||
|
||||
try {
|
||||
const config = yaml.load(processedContent) as PromptConfig
|
||||
@@ -86,9 +73,7 @@ export function loadPromptFile(
|
||||
// Validate messages
|
||||
for (const message of config.messages) {
|
||||
if (!message.role || !message.content) {
|
||||
throw new Error(
|
||||
'Each message must have "role" and "content" properties'
|
||||
)
|
||||
throw new Error('Each message must have "role" and "content" properties')
|
||||
}
|
||||
if (!['system', 'user', 'assistant'].includes(message.role)) {
|
||||
throw new Error(`Invalid message role: ${message.role}`)
|
||||
@@ -97,9 +82,7 @@ export function loadPromptFile(
|
||||
|
||||
return config
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to parse prompt file: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
throw new Error(`Failed to parse prompt file: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,5 @@
|
||||
"noEmit": true
|
||||
},
|
||||
"exclude": ["dist", "node_modules"],
|
||||
"include": [
|
||||
"__fixtures__",
|
||||
"__tests__",
|
||||
"src",
|
||||
"eslint.config.mjs",
|
||||
"jest.config.js",
|
||||
"rollup.config.ts"
|
||||
]
|
||||
"include": ["__fixtures__", "__tests__", "src", "eslint.config.mjs", "rollup.config.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user