122 Commits
v1 ... v2

Author SHA1 Message Date
Stephanie Giang
e09e659817 Merge pull request #173 from GitPaulo/main
Support passing max_tokens and max_completion_tokens
2026-02-24 10:40:15 -05:00
Paulo Santos
e608d2ba8a update dist 2026-02-15 00:26:15 +00:00
Paulo Santos
27965bc3a4 updated docs for missing prompt.yml model parameters 2026-02-15 00:23:47 +00:00
Paulo Santos
a8bddad5e5 update dist 2026-02-13 12:41:36 +00:00
Paulo Santos
672ba8a3ac missed comment 2026-02-13 12:38:48 +00:00
Paulo Santos
3a80d137e1 update comments 2026-02-13 12:36:47 +00:00
Paulo Santos
074e8b294d copilot review: add test for coverage of no params passed 2026-02-13 12:31:45 +00:00
Paulo Santos
f1ca66fc66 build dist 2026-02-13 12:16:03 +00:00
Paulo Santos
6360e0db9b implement passing two action input properties to cover all model scenarios 2026-02-13 12:15:12 +00:00
Stephanie Giang
a380166897 Merge pull request #170 from GitPaulo/gitpaulo/update-deprecated-max-tokens
Update deprecated max_tokens to max_completion_tokens
2026-02-06 11:09:51 -05:00
Paulo Santos
b07a08c5eb generate dist 2026-02-04 21:30:14 +00:00
Paulo Santos
725fb1c850 update max_tokens to max_completion_tokens 2026-02-04 21:29:53 +00:00
Stephanie Giang
95f4a27227 Merge pull request #160 from actions/dependabot/npm_and_yarn/rollup/rollup-linux-x64-gnu-4.55.1
chore(deps): bump @rollup/rollup-linux-x64-gnu from 4.52.5 to 4.55.1
2026-02-04 16:04:14 -05:00
dependabot[bot]
b9877e2b39 chore(deps): bump @rollup/rollup-linux-x64-gnu from 4.52.5 to 4.55.1
Bumps [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) from 4.52.5 to 4.55.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.52.5...v4.55.1)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.55.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-04 21:00:34 +00:00
Stephanie Giang
29ac79522a Merge pull request #164 from actions/dependabot/npm_and_yarn/lodash-4.17.23
chore(deps): bump lodash from 4.17.21 to 4.17.23
2026-02-04 15:59:45 -05:00
Stephanie Giang
4ae036562a Merge branch 'main' into dependabot/npm_and_yarn/lodash-4.17.23 2026-02-04 15:59:12 -05:00
Stephanie Giang
268593b9a6 Merge pull request #168 from GitPaulo/gitpaulo/fork-add-temperature-topp-params
Add model parameters temperature and topP to action inputs
2026-02-04 15:58:13 -05:00
Paulo Santos
1171309110 refactor temperature/top-p parsing for clarity 2026-02-04 12:20:53 +00:00
Paulo Santos
71c69d42b5 document temperature and top-p inputs in readme 2026-02-04 12:12:24 +00:00
Paulo Santos
d51321a7a6 rebuild dist bundle 2026-02-04 12:12:19 +00:00
Paulo Santos
5b62ecd0dd add temperature and top-p input parameters 2026-02-04 12:12:14 +00:00
dependabot[bot]
eff4de28e3 chore(deps): bump lodash from 4.17.21 to 4.17.23
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-23 17:09:22 +00:00
Sean Goedecke
a6101c89c6 Merge pull request #163 from yg1996/add-custom-headers-support
Add custom headers support for API Management integration
2026-01-19 17:28:08 +11:00
Yonatan Golick
15ae50ae2f Add CRLF injection protection for header values
Implement security validation to prevent HTTP header injection attacks:
- Reject header values containing \r or \n characters
- Add comprehensive test coverage for CRLF protection
- Replace multiline YAML test with proper rejection test

Security improvements:
- Validates header values to prevent header injection
- Clear warning messages when values are rejected
- Four new test cases covering LF, CR, CRLF, and multiline scenarios

This addresses a critical security concern where malicious headers
could be injected via newline characters in header values.

All 84 tests passing.
2026-01-18 12:19:43 +02:00
Yonatan Golick
f77380037b Update src/helpers.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-18 11:59:12 +02:00
Yonatan Golick
6402ff8f9a Update README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-18 11:58:58 +02:00
Yonatan Golick
c760995fbc Remove redundant feature documentation file
Delete CUSTOM_HEADERS_FEATURE.md as the README.md already contains
comprehensive documentation for the custom headers feature. This
reduces documentation duplication and follows standard practice
of keeping feature docs in the README.
2026-01-18 11:43:39 +02:00
Yonatan Golick
ce720b3d0c Fix header validation per RFC 7230 and add null check
Address Copilot AI feedback:
- Remove underscore support from header names (RFC 7230 compliance)
- Add explicit null check for JSON parsing
- Update validation regex to /^[A-Za-z0-9-]+$/
- Add test case for null value handling
- Update documentation to clarify header name requirements

Changes:
- Header names now only accept alphanumeric characters and hyphens
- Improved error messages for invalid headers
- Added test for null JSON input
- Updated APIM example tests

All 81 tests passing.
2026-01-18 11:35:18 +02:00
Yonatan Golick
6d144ac474 Add custom headers support for API Management integration
This change adds support for custom HTTP headers in AI inference requests,
enabling integration with API Management platforms (Azure APIM, AWS API
Gateway, Kong, etc.) and custom request routing/tracking.

Features:
- New 'custom-headers' input supporting both YAML and JSON formats
- Auto-detection of input format for better UX
- Header name validation (alphanumeric, hyphens, underscores)
- Automatic masking of sensitive headers in logs
- Full backward compatibility (optional parameter)

Changes:
- Added parseCustomHeaders() function in helpers.ts
- Updated InferenceRequest interface with optional customHeaders field
- Modified simpleInference() and mcpInference() to pass headers to OpenAI client
- Added 18 comprehensive test cases
- Updated documentation with examples and use cases

All 80 tests passing. Zero breaking changes.
2026-01-18 11:24:13 +02:00
Sarah Vessels
63993128d7 Merge pull request #51 from KyFaSt/patch-1
Add Missing Languages to CodeQL Advanced Configuration
2026-01-07 11:27:48 -06:00
Sarah Vessels
3dfda414c6 Merge branch 'main' into patch-1 2026-01-07 11:21:35 -06:00
Sarah Vessels
b99f473284 Merge pull request #152 from actions/dependabot/npm_and_yarn/express-5.2.1
chore(deps): bump express from 5.1.0 to 5.2.1
2026-01-07 11:11:33 -06:00
Sarah Vessels
acb23a78e0 Merge branch 'main' into dependabot/npm_and_yarn/express-5.2.1 2026-01-07 11:10:57 -06:00
Sarah Vessels
de9f3a655a Merge pull request #158 from actions/dependabot/npm_and_yarn/modelcontextprotocol/sdk-1.25.2
chore(deps): bump @modelcontextprotocol/sdk from 1.24.0 to 1.25.2
2026-01-07 11:09:33 -06:00
Sarah Vessels
38e68a8ffc licensed cache 2026-01-07 11:01:16 -06:00
Sarah Vessels
7012ba221f Merge branch 'main' into dependabot/npm_and_yarn/express-5.2.1 2026-01-07 11:00:24 -06:00
Sarah Vessels
185fd7e675 licensed cache 2026-01-07 10:57:30 -06:00
Sarah Vessels
d89080af40 npm run bundle 2026-01-07 10:57:20 -06:00
dependabot[bot]
6f1b0370bb chore(deps): bump @modelcontextprotocol/sdk from 1.24.0 to 1.25.2
Bumps [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) from 1.24.0 to 1.25.2.
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/1.24.0...v1.25.2)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.25.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-07 16:51:03 +00:00
Sarah Vessels
0a593b0c24 Merge pull request #149 from actions/dependabot/npm_and_yarn/body-parser-2.2.1
chore(deps): bump body-parser from 2.2.0 to 2.2.1
2026-01-06 14:12:53 -06:00
Sarah Vessels
214f0b0a68 Merge branch 'main' into dependabot/npm_and_yarn/body-parser-2.2.1 2026-01-06 14:12:17 -06:00
Sarah Vessels
812f8bb844 Merge pull request #144 from actions/dependabot/npm_and_yarn/js-yaml-4.1.1
chore(deps): bump js-yaml from 4.1.0 to 4.1.1
2026-01-06 14:11:41 -06:00
Sarah Vessels
de36aa9302 npm run bundle 2026-01-06 14:02:26 -06:00
Sarah Vessels
b99132354c Merge branch 'main' into dependabot/npm_and_yarn/js-yaml-4.1.1 2026-01-06 14:01:47 -06:00
Sarah Vessels
ad4351a3a2 npm run bundle 2026-01-06 13:57:11 -06:00
Sarah Vessels
146434d459 Merge pull request #153 from actions/dependabot/npm_and_yarn/modelcontextprotocol/sdk-1.24.0
chore(deps): bump @modelcontextprotocol/sdk from 1.15.1 to 1.24.0
2026-01-06 13:56:25 -06:00
Sarah Vessels
ce17fb5d9d licensed cache 2026-01-06 13:53:13 -06:00
Sarah Vessels
2ef8c2618e Merge branch 'main' into dependabot/npm_and_yarn/js-yaml-4.1.1 2026-01-06 13:52:50 -06:00
Sarah Vessels
eeef9fe1ab Merge branch 'main' into dependabot/npm_and_yarn/modelcontextprotocol/sdk-1.24.0 2026-01-06 13:51:06 -06:00
Sarah Vessels
82e525eb0f licensed cache 2026-01-06 13:47:43 -06:00
Sarah Vessels
0f8b89d701 Merge branch 'main' into dependabot/npm_and_yarn/body-parser-2.2.1 2026-01-06 13:47:02 -06:00
Sarah Vessels
7e91a1e627 Merge pull request #132 from actions/dependabot/npm_and_yarn/rollup/rollup-linux-x64-gnu-4.52.5
chore(deps): bump @rollup/rollup-linux-x64-gnu from 4.46.0 to 4.52.5
2026-01-06 13:45:33 -06:00
Sarah Vessels
82a7737224 Merge branch 'main' into dependabot/npm_and_yarn/rollup/rollup-linux-x64-gnu-4.52.5 2026-01-06 13:45:00 -06:00
Sarah Vessels
6709541cb1 Merge pull request #135 from actions/dependabot/npm_and_yarn/vite-7.1.11
chore(deps-dev): bump vite from 7.0.6 to 7.1.11
2026-01-06 13:44:04 -06:00
Sarah Vessels
059db2ba93 Merge branch 'main' into dependabot/npm_and_yarn/vite-7.1.11 2026-01-06 13:43:32 -06:00
Sarah Vessels
7228fd1adf npm run bundle 2026-01-06 13:37:09 -06:00
Sarah Vessels
04be163c40 licensed cache 2026-01-06 13:34:55 -06:00
Sarah Vessels
26b055458c Merge branch 'main' into dependabot/npm_and_yarn/modelcontextprotocol/sdk-1.24.0 2026-01-06 13:33:53 -06:00
Sarah Vessels
42a45bceaf Merge pull request #157 from actions/dependabot/npm_and_yarn/qs-6.14.1
chore(deps): bump qs from 6.14.0 to 6.14.1
2026-01-06 13:30:43 -06:00
Sarah Vessels
52a34c2089 licensed cache 2026-01-06 13:23:30 -06:00
dependabot[bot]
cab0b8c09d chore(deps): bump qs from 6.14.0 to 6.14.1
Bumps [qs](https://github.com/ljharb/qs) from 6.14.0 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-06 19:11:09 +00:00
Sarah Vessels
340ab189bd Merge pull request #146 from actions/dependabot/npm_and_yarn/glob-10.5.0
chore(deps): bump glob from 10.4.5 to 10.5.0
2026-01-06 13:10:01 -06:00
dependabot[bot]
e783798b50 chore(deps): bump @modelcontextprotocol/sdk from 1.15.1 to 1.24.0
Bumps [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) from 1.15.1 to 1.24.0.
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/1.15.1...1.24.0)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.24.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-02 17:18:14 +00:00
dependabot[bot]
ff49162b2d chore(deps): bump express from 5.1.0 to 5.2.1
Bumps [express](https://github.com/expressjs/express) from 5.1.0 to 5.2.1.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/v5.1.0...v5.2.1)

---
updated-dependencies:
- dependency-name: express
  dependency-version: 5.2.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 23:13:26 +00:00
dependabot[bot]
fd45b46eda chore(deps): bump body-parser from 2.2.0 to 2.2.1
Bumps [body-parser](https://github.com/expressjs/body-parser) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/v2.2.0...v2.2.1)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-version: 2.2.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 02:58:14 +00:00
dependabot[bot]
efe413bb31 chore(deps): bump glob from 10.4.5 to 10.5.0
Bumps [glob](https://github.com/isaacs/node-glob) from 10.4.5 to 10.5.0.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.4.5...v10.5.0)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 10.5.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 02:56:58 +00:00
dependabot[bot]
82fb91a5d5 chore(deps): bump js-yaml from 4.1.0 to 4.1.1
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 02:46:34 +00:00
Sean Goedecke
334892bb20 Merge pull request #142 from maartenvandiemen/feature/pass-toolsets
Pass GitHub MCP Tools
2025-12-01 08:48:16 +11:00
Maarten van Diemen
bbe0ccb244 Fix failing tests 2025-11-30 22:20:19 +01:00
Maarten van Diemen
ca3b99ea74 Undo changes in tests.
Undo linter change
2025-11-30 22:14:02 +01:00
Maarten van Diemen
8a5d2ea4a1 Merge branch 'main' into feature/pass-toolsets 2025-11-30 22:08:41 +01:00
Sean Goedecke
112739fb15 Merge pull request #139 from GulerSevil/patch-1
Clarify token requirements for MCP integration
2025-12-01 05:35:40 +11:00
Sevil
f95554969e Merge branch 'main' into patch-1 2025-11-29 21:47:36 +01:00
Sevil
9e60aa0a3f Lint fix
Lint fix
2025-11-29 21:15:11 +01:00
Sean Goedecke
02c6cc30ae Merge pull request #150 from actions/sgoedecke/mock-inference-in-ci
Mock inference in CI
2025-11-28 08:17:33 +11:00
Sean Goedecke
18d468666d fix: keep response-file temp file for downstream steps
The temporary file created for response-file was being cleaned up
before downstream steps could access it. Now using keep: true to
ensure the file persists until the job completes.

Also added script/ to eslint ignores for the mock server.
2025-11-27 21:06:42 +00:00
Sean Goedecke
fd73d0264c Mock inference in CI 2025-11-27 20:59:41 +00:00
Maarten van Diemen
27350b2a98 Merge branch 'feature/pass-toolsets' of https://github.com/maartenvandiemen/ai-inference into feature/pass-toolsets 2025-11-26 22:58:36 +01:00
Maarten van Diemen
e8987e92e0 Fix linter
Update GitHub Actions
2025-11-26 22:58:31 +01:00
Sevil
2d03946378 Merge branch 'main' into patch-1 2025-11-25 15:01:12 +01:00
Sevil
d061fc5469 Update README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-25 14:59:33 +01:00
Maarten van Diemen
2d2f67ec42 Update CI workflow to trigger on pull requests and pushes 2025-11-24 13:22:20 +01:00
Maarten van Diemen
9170087739 Update CI workflow to use manual trigger only
Removed automatic triggers for pull requests and pushes to main branch.
2025-11-24 13:19:46 +01:00
Maarten van Diemen
62db90ab13 Upgrade checkout to V6 2025-11-24 13:16:10 +01:00
Maarten van Diemen
16f2d5c46b Merge with main 2025-11-24 13:12:28 +01:00
Maarten van Diemen
95443f8d18 Merge with main 2025-11-24 13:06:01 +01:00
Sean Goedecke
5022b33bc1 Merge pull request #148 from dsanders11/feat/prompt-yaml-model-parameters
feat: support modelParameters in prompt.yaml files
2025-11-24 11:27:47 +11:00
David Sanders
c9e14713bc chore: update dist 2025-11-23 16:19:48 -08:00
David Sanders
39308142df chore: apply code review comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-23 16:19:12 -08:00
David Sanders
48f0edec4d feat: support modelParameters in prompt.yaml files 2025-11-23 16:07:11 -08:00
Sean Goedecke
36ea1371dc Merge pull request #136 from dsanders11/fix/template-substition
fix: do template substition after parsing prompt YAML
2025-11-24 10:22:42 +11:00
Sean Goedecke
de16a30c20 Merge branch 'main' into fix/template-substition 2025-11-24 10:21:49 +11:00
Sean Goedecke
48758ceaff Merge branch 'main' into feature/pass-toolsets 2025-11-24 10:19:55 +11:00
Sean Goedecke
dd3dff10ba Merge pull request #147 from srt32/patch-1
Clarify PAT requirement for github-mcp-token
2025-11-24 10:18:50 +11:00
Simon Taranto
4bb01ee5ee Clarify PAT requirement for github-mcp-token
I mistakenly read the description of the mcp-token field to mean I needed a "PAT for MCP" as if there were a PAT permission for MCP. This change clarifies the language.
2025-11-21 13:36:45 -05:00
Maarten van Diemen
4b4b2e8afe build index.js 2025-11-02 23:37:23 +01:00
Maarten van Diemen
932a853db4 Initial implementation for passing toolsets 2025-11-02 23:20:24 +01:00
Sevil
e0da58c63f Clarify token requirements for MCP integration
Updated authentication section to clarify token usage for MCP integration.
2025-10-25 22:53:45 +02:00
David Sanders
af1c1c29a3 fix: do template substition after parsing prompt YAML 2025-10-20 21:32:06 -07:00
dependabot[bot]
7688a5060c chore(deps-dev): bump vite from 7.0.6 to 7.1.11
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.6 to 7.1.11.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.11/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.11
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 21:58:17 +00:00
dependabot[bot]
52a32ececf chore(deps): bump @rollup/rollup-linux-x64-gnu from 4.46.0 to 4.52.5
Bumps [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) from 4.46.0 to 4.52.5.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.46.0...v4.52.5)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.52.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 02:20:45 +00:00
Sean Goedecke
83bb5ca3e8 Merge pull request #93 from FidelusAleksander/main
docs: update documentation on mcp usage
2025-08-26 18:13:39 +10:00
Aleksander Fidelus
4d2337d006 Merge branch 'actions:main' into main 2025-08-25 11:08:41 +02:00
Yuzuki
7ba7530ad4 Merge pull request #94 from actions/dependabot/github_actions/actions/checkout-5
chore(deps): bump actions/checkout from 4 to 5
2025-08-25 14:00:39 +10:00
Yuzuki
4d7d83c494 Merge branch 'main' into dependabot/github_actions/actions/checkout-5 2025-08-25 13:55:57 +10:00
Sean Goedecke
a1c1182922 Merge pull request #97 from actions/sgoedecke/defensive-parsing
Parse inference response format defensively
2025-08-25 08:47:18 +10:00
Sean Goedecke
dfaa426c29 Parse inference response format defensively 2025-08-22 22:34:18 +00:00
FidelusAleksander
7fa0024f13 docs: run prettier 2025-08-18 14:42:29 +02:00
dependabot[bot]
fc6f9a0800 chore(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-18 03:58:02 +00:00
FidelusAleksander
a1d07305b7 docs: update github-mcp-token description 2025-08-15 08:22:55 +02:00
FidelusAleksander
6e0d8949d8 docs: update documentation on mcp usage 2025-08-15 07:52:22 +02:00
Sean Goedecke
f347eae8eb Merge pull request #91 from JessRudder/secure-tmp-files
Uses tmp library to ensure more secure tmp file creation
2025-08-14 07:15:18 +10:00
Jess Rudder
07fe2f30ad Merge branch 'main' into secure-tmp-files 2025-08-13 14:11:23 -07:00
Jess Rudder
1843310df4 Add license info 2025-08-13 21:07:21 +00:00
Sean Goedecke
c72cb2ef9c Merge pull request #90 from garman/pin-to-sha
Pin two imported actions to a set sha
2025-08-14 06:58:03 +10:00
Jessica Rudder
a2fd223fcf Properly clean up tmp files 2025-08-12 14:31:05 -07:00
Jessica Rudder
3ba8e1b39d Replace manual tmp file creation with tmp library which uses security best practices 2025-08-12 13:49:47 -07:00
Daniel Garman
52e5222a82 pin to a sha 2025-08-12 15:04:16 -04:00
Sean Goedecke
a62dfeda7b Merge pull request #79 from salmanmkc/node24
Node 24
2025-08-11 21:13:39 +10:00
Salman Chishti
48235f7026 Merge branch 'main' into node24 2025-08-11 11:52:36 +01:00
Salman Muin Kayser Chishti
9bbcef8fa4 node 24 2025-08-01 12:13:15 +01:00
Kylie Stradley
411276b07e Add Missing Languages to CodeQL Advanced Configuration 2025-07-10 09:20:46 -04:00
49 changed files with 23610 additions and 12550 deletions

View File

@@ -28,11 +28,11 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: .node-version
cache: npm
@@ -66,7 +66,7 @@ jobs:
- if: ${{ failure() && steps.diff.outcome == 'failure' }}
name: Upload Artifact
id: upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: dist
path: dist/

View File

@@ -20,11 +20,11 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: .node-version
cache: npm
@@ -54,22 +54,53 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Start Mock Inference Server
id: mock-server
run: |
node script/mock-inference-server.mjs &
echo "pid=$!" >> $GITHUB_OUTPUT
# Wait for server to be ready
for i in {1..10}; do
if curl -s http://localhost:3456/health > /dev/null; then
echo "Mock server is ready"
break
fi
sleep 1
done
- name: Test Local Action
id: test-action
continue-on-error: true
uses: ./
with:
prompt: hello
endpoint: http://localhost:3456
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Print Output
id: output
continue-on-error: true
run: echo "${{ steps.test-action.outputs.response }}"
- name: Verify Output
run: |
response="${{ steps.test-action.outputs.response }}"
if [[ -z "$response" ]]; then
echo "Error: No response received"
exit 1
fi
echo "Response received: $response"
- name: Stop Mock Server
if: always()
run: kill ${{ steps.mock-server.outputs.pid }} || true
test-action-prompt-file:
name: GitHub Actions Test with Prompt File
runs-on: ubuntu-latest
@@ -77,7 +108,26 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Start Mock Inference Server
id: mock-server
run: |
node script/mock-inference-server.mjs &
echo "pid=$!" >> $GITHUB_OUTPUT
# Wait for server to be ready
for i in {1..10}; do
if curl -s http://localhost:3456/health > /dev/null; then
echo "Mock server is ready"
break
fi
sleep 1
done
- name: Create Prompt File
run: echo "hello" > prompt.txt
@@ -87,16 +137,33 @@ jobs:
- name: Test Local Action with Prompt File
id: test-action-prompt-file
continue-on-error: true
uses: ./
with:
prompt-file: prompt.txt
system-prompt-file: system-prompt.txt
endpoint: http://localhost:3456
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Print Output
continue-on-error: true
run: |
echo "Response saved to: ${{ steps.test-action-prompt-file.outputs.response-file }}"
cat "${{ steps.test-action-prompt-file.outputs.response-file }}"
- name: Verify Output
run: |
response_file="${{ steps.test-action-prompt-file.outputs.response-file }}"
if [[ ! -f "$response_file" ]]; then
echo "Error: Response file not found"
exit 1
fi
content=$(cat "$response_file")
if [[ -z "$content" ]]; then
echo "Error: Response file is empty"
exit 1
fi
echo "Response file content: $content"
- name: Stop Mock Server
if: always()
run: kill ${{ steps.mock-server.outputs.pid }} || true

View File

@@ -25,24 +25,24 @@ jobs:
fail-fast: false
matrix:
language:
- TypeScript
- TypeScript, actions
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Initialize CodeQL
id: initialize
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
source-root: src
- name: Autobuild
id: autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
id: analyze
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4

View File

@@ -27,11 +27,11 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: .node-version
cache: npm
@@ -42,11 +42,11 @@ jobs:
- name: Setup Ruby
id: setup-ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71
with:
ruby-version: ruby
- uses: licensee/setup-licensed@v1.3.2
- uses: licensee/setup-licensed@0d52e575b3258417672be0dff2f115d7db8771d8
with:
version: 4.x
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

View File

@@ -0,0 +1,11 @@
---
name: "@hono/node-server"
version: 1.19.7
type: npm
summary: Node.js Adapter for Hono
homepage: https://github.com/honojs/node-server
license: mit
licenses:
- sources: README.md
text: MIT
notices: []

View File

@@ -1,6 +1,6 @@
---
name: "@modelcontextprotocol/sdk"
version: 1.15.1
version: 1.25.2
type: npm
summary: Model Context Protocol implementation for TypeScript
homepage: https://modelcontextprotocol.io

View File

@@ -0,0 +1,32 @@
---
name: "@types/tmp"
version: 0.2.6
type: npm
summary: TypeScript definitions for tmp
homepage: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/tmp
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: []

View File

@@ -0,0 +1,34 @@
---
name: ajv-formats
version: 3.0.1
type: npm
summary: Format validation for Ajv v7+
homepage: https://github.com/ajv-validator/ajv-formats#readme
license: mit
licenses:
- sources: LICENSE
text: |
MIT License
Copyright (c) 2020 Evgeny Poberezkin
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](https://github.com/ajv-validator/ajv-formats/blob/master/LICENSE)"
notices: []

View File

@@ -1,16 +1,16 @@
---
name: ajv
version: 6.12.6
version: 8.17.1
type: npm
summary: Another JSON Schema Validator
homepage: https://github.com/ajv-validator/ajv
homepage: https://ajv.js.org
license: mit
licenses:
- sources: LICENSE
text: |+
The MIT License (MIT)
Copyright (c) 2015-2017 Evgeny Poberezkin
Copyright (c) 2015-2021 Evgeny Poberezkin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -31,6 +31,6 @@ licenses:
SOFTWARE.
- sources: README.md
text: "[MIT](https://github.com/ajv-validator/ajv/blob/master/LICENSE)"
text: "[MIT](./LICENSE)"
notices: []
...

View File

@@ -1,6 +1,6 @@
---
name: body-parser
version: 2.2.0
version: 2.2.1
type: npm
summary: Node.js body parsing middleware
homepage:
@@ -35,15 +35,13 @@ licenses:
text: |-
[MIT](LICENSE)
[ci-image]: https://badgen.net/github/checks/expressjs/body-parser/master?label=ci
[ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/body-parser/ci.yml?branch=master&label=ci
[ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/body-parser/master
[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/body-parser?branch=master
[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
[node-version-image]: https://badgen.net/npm/node/body-parser
[node-version-url]: https://nodejs.org/en/download
[npm-downloads-image]: https://badgen.net/npm/dm/body-parser
[npm-downloads-image]: https://img.shields.io/npm/dm/body-parser
[npm-url]: https://npmjs.org/package/body-parser
[npm-version-image]: https://badgen.net/npm/v/body-parser
[npm-version-image]: https://img.shields.io/npm/v/body-parser
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/body-parser/badge
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/body-parser
notices: []

View File

@@ -1,6 +1,6 @@
---
name: debug
version: 4.4.1
version: 4.4.3
type: npm
summary: Lightweight debugging utility for Node.js and the browser
homepage:

View File

@@ -1,6 +1,6 @@
---
name: express
version: 5.1.0
version: 5.2.1
type: npm
summary: Fast, unopinionated, minimalist web framework
homepage: https://expressjs.com/
@@ -36,15 +36,16 @@ licenses:
text: |-
[MIT](LICENSE)
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/express/master
[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/express?branch=master
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=CI
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/express/ci.yml?branch=master&label=ci
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
[npm-downloads-image]: https://badgen.net/npm/dm/express
[npm-downloads-image]: https://img.shields.io/npm/dm/express
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
[npm-url]: https://npmjs.org/package/express
[npm-version-image]: https://badgen.net/npm/v/express
[npm-version-image]: https://img.shields.io/npm/v/express
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express
[Code of Conduct]: https://github.com/expressjs/express/blob/master/Code-Of-Conduct.md
[Code of Conduct]: https://github.com/expressjs/.github/blob/HEAD/CODE_OF_CONDUCT.md
[Contributing Guide]: https://github.com/expressjs/.github/blob/HEAD/CONTRIBUTING.md
notices: []

View File

@@ -1,35 +0,0 @@
---
name: fast-json-stable-stringify
version: 2.1.0
type: npm
summary: deterministic `JSON.stringify()` - a faster version of substack's json-stable-strigify
without jsonify
homepage: https://github.com/epoberezkin/fast-json-stable-stringify
license: other
licenses:
- sources: LICENSE
text: |
This software is released under the MIT license:
Copyright (c) 2017 Evgeny Poberezkin
Copyright (c) 2013 James Halliday
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](https://github.com/epoberezkin/fast-json-stable-stringify/blob/master/LICENSE)"
notices: []

View File

@@ -0,0 +1,45 @@
---
name: fast-uri
version: 3.1.0
type: npm
summary: Dependency-free RFC 3986 URI toolbox
homepage: https://github.com/fastify/fast-uri
license: other
licenses:
- sources: LICENSE
text: |-
Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae
Copyright (c) 2021-present The Fastify team
All rights reserved.
The Fastify team members are listed at https://github.com/fastify/fastify#team.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of any contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * *
The complete list of contributors can be found at:
- https://github.com/garycourt/uri-js/graphs/contributors
- sources: README.md
text: Licensed under [BSD-3-Clause](./LICENSE).
notices: []

View File

@@ -0,0 +1,34 @@
---
name: hono
version: 4.11.3
type: npm
summary: Web framework built on Web Standards
homepage: https://hono.dev
license: mit
licenses:
- sources: LICENSE
text: |
MIT License
Copyright (c) 2021 - present, Yusuke Wada and Hono 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: Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
notices: []

View File

@@ -1,6 +1,6 @@
---
name: http-errors
version: 2.0.0
version: 2.0.1
type: npm
summary: Create HTTP error objects
homepage:

View File

@@ -1,9 +1,9 @@
---
name: iconv-lite
version: 0.6.3
version: 0.7.0
type: npm
summary: Convert character encodings in pure javascript.
homepage: https://github.com/ashtuchkin/iconv-lite
homepage: https://github.com/pillarjs/iconv-lite
license: mit
licenses:
- sources: LICENSE

View File

@@ -0,0 +1,33 @@
---
name: jose
version: 6.1.3
type: npm
summary: JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno,
Bun, and other Web-interoperable runtimes
homepage: https://github.com/panva/jose
license: mit
licenses:
- sources: LICENSE.md
text: |
The MIT License (MIT)
Copyright (c) 2018 Filip Skokan
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: []

View File

@@ -1,6 +1,6 @@
---
name: js-yaml
version: 4.1.0
version: 4.1.1
type: npm
summary: YAML 1.2 parser and serializer
homepage:

View File

@@ -1,6 +1,6 @@
---
name: json-schema-traverse
version: 0.4.1
version: 1.0.0
type: npm
summary: Traverse JSON Schema passing each schema object to callback
homepage: https://github.com/epoberezkin/json-schema-traverse#readme

View File

@@ -0,0 +1,73 @@
---
name: json-schema-typed
version: 8.0.2
type: npm
summary: JSON Schema TypeScript definitions with complete inline documentation.
homepage: https://github.com/RemyRylan/json-schema-typed/tree/main/dist/node
license: other
licenses:
- sources: LICENSE.md
text: |
BSD 2-Clause License
Original source code is copyright (c) 2019-2025 Remy Rylan
<https://github.com/RemyRylan>
All JSON Schema documentation and descriptions are copyright (c):
2009 [draft-0] IETF Trust <https://www.ietf.org/>, Kris Zyp <kris@sitepen.com>,
and SitePen (USA) <https://www.sitepen.com/>.
2009 [draft-1] IETF Trust <https://www.ietf.org/>, Kris Zyp <kris@sitepen.com>,
and SitePen (USA) <https://www.sitepen.com/>.
2010 [draft-2] IETF Trust <https://www.ietf.org/>, Kris Zyp <kris@sitepen.com>,
and SitePen (USA) <https://www.sitepen.com/>.
2010 [draft-3] IETF Trust <https://www.ietf.org/>, Kris Zyp <kris@sitepen.com>,
Gary Court <gary.court@gmail.com>, and SitePen (USA) <https://www.sitepen.com/>.
2013 [draft-4] IETF Trust <https://www.ietf.org/>), Francis Galiegue
<fgaliegue@gmail.com>, Kris Zyp <kris@sitepen.com>, Gary Court
<gary.court@gmail.com>, and SitePen (USA) <https://www.sitepen.com/>.
2018 [draft-7] IETF Trust <https://www.ietf.org/>, Austin Wright <aaa@bzfx.net>,
Henry Andrews <henry@cloudflare.com>, Geraint Luff <luffgd@gmail.com>, and
Cloudflare, Inc. <https://www.cloudflare.com/>.
2019 [draft-2019-09] IETF Trust <https://www.ietf.org/>, Austin Wright
<aaa@bzfx.net>, Henry Andrews <andrews_henry@yahoo.com>, Ben Hutton
<bh7@sanger.ac.uk>, and Greg Dennis <gregsdennis@yahoo.com>.
2020 [draft-2020-12] IETF Trust <https://www.ietf.org/>, Austin Wright
<aaa@bzfx.net>, Henry Andrews <andrews_henry@yahoo.com>, Ben Hutton
<ben@jsonschema.dev>, and Greg Dennis <gregsdennis@yahoo.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- sources: README.md
text: |-
[BSD-2-Clause][license]
[license]: https://github.com/RemyRylan/json-schema-typed/blob/main/dist/node/LICENSE.md
notices: []

View File

@@ -1,34 +0,0 @@
---
name: punycode
version: 2.3.1
type: npm
summary: A robust Punycode converter that fully complies to RFC 3492 and RFC 5891,
and works on nearly all JavaScript platforms.
homepage: https://mths.be/punycode
license: mit
licenses:
- sources: LICENSE-MIT.txt
text: |
Copyright Mathias Bynens <https://mathiasbynens.be/>
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: Punycode.js is available under the [MIT](https://mths.be/mit) license.
notices: []

View File

@@ -1,6 +1,6 @@
---
name: qs
version: 6.14.0
version: 6.14.1
type: npm
summary: A querystring parser that supports nesting and arrays, with a depth limit
homepage: https://github.com/ljharb/qs

View File

@@ -1,6 +1,6 @@
---
name: raw-body
version: 3.0.0
version: 3.0.2
type: npm
summary: Get and validate the raw body of a readable stream.
homepage:

View File

@@ -1,18 +1,16 @@
---
name: statuses
version: 2.0.1
name: require-from-string
version: 2.0.2
type: npm
summary: HTTP status utility
summary: Require module from string
homepage:
license: mit
licenses:
- sources: LICENSE
text: |2
- sources: license
text: |
The MIT License (MIT)
Copyright (c) 2014 Jonathan Ong <me@jongleberry.com>
Copyright (c) 2016 Douglas Christopher Wilson <doug@somethingdoug.com>
Copyright (c) Vsevolod Strukchinsky <floatdrop@gmail.com> (github.com/floatdrop)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -31,17 +29,6 @@ licenses:
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](LICENSE)
[ci-image]: https://badgen.net/github/checks/jshttp/statuses/master?label=ci
[ci-url]: https://github.com/jshttp/statuses/actions?query=workflow%3Aci
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/statuses/master
[coveralls-url]: https://coveralls.io/r/jshttp/statuses?branch=master
[node-version-image]: https://badgen.net/npm/node/statuses
[node-version-url]: https://nodejs.org/en/download
[npm-downloads-image]: https://badgen.net/npm/dm/statuses
[npm-url]: https://npmjs.org/package/statuses
[npm-version-image]: https://badgen.net/npm/v/statuses
- sources: readme.md
text: MIT © [Vsevolod Strukchinsky](http://github.com/floatdrop)
notices: []

32
.licenses/npm/tmp.dep.yml Normal file
View File

@@ -0,0 +1,32 @@
---
name: tmp
version: 0.2.5
type: npm
summary: Temporary file and directory creator
homepage: http://github.com/raszi/node-tmp
license: mit
licenses:
- sources: LICENSE
text: |
The MIT License (MIT)
Copyright (c) 2014 KARASZI Istvá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:
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: []

View File

@@ -1,29 +0,0 @@
---
name: uri-js
version: 4.4.1
type: npm
summary: An RFC 3986/3987 compliant, scheme extendable URI/IRI parsing/validating/resolving
library for JavaScript.
homepage: https://github.com/garycourt/uri-js
license: other
licenses:
- sources: LICENSE
text: "Copyright 2011 Gary Court. All rights reserved.\n\nRedistribution and use
in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:\n\n1.\tRedistributions of source code must
retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2.\tRedistributions
in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided
with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY GARY COURT \"AS IS\" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL GARY COURT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.\n\nThe views and conclusions contained in the software and documentation
are those of the authors and should not be interpreted as representing official
policies, either expressed or implied, of Gary Court.\n"
notices: []

View File

@@ -1,6 +1,6 @@
---
name: zod-to-json-schema
version: 3.24.6
version: 3.25.0
type: npm
summary: Converts Zod schemas to Json Schemas
homepage:

View File

@@ -1 +1 @@
20.9.0
24.4.0

143
README.md
View File

@@ -123,6 +123,33 @@ supplied via the `input` parameter in YAML format. Additionally, you can
provide file-based variables via `file_input`, where each key maps to a file
path.
### Prompt.yml with model parameters
You can specify model parameters directly in your `.prompt.yml` files using the
`modelParameters` key:
```yaml
messages:
- role: system
content: Be as concise as possible
- role: user
content: 'Compare {{a}} and {{b}}, please'
model: openai/gpt-4o
modelParameters:
maxCompletionTokens: 500
temperature: 0.7
```
| Key | Type | Description |
| --------------------- | ------ | ----------------------------------------------------- |
| `maxCompletionTokens` | number | The maximum number of tokens to generate |
| `maxTokens` | number | The maximum number of tokens to generate (deprecated) |
| `temperature` | number | The sampling temperature to use (0-1) |
| `topP` | number | The nucleus sampling parameter to use (0-1) |
> ![Note]
> Parameters set in `modelParameters` take precedence over the corresponding action inputs.
### Using a system prompt file
In addition to the regular prompt, you can provide a system prompt file instead
@@ -156,12 +183,73 @@ steps:
cat "${{ steps.inference.outputs.response-file }}"
```
### Using custom headers
You can include custom HTTP headers in your API requests, which is useful for integrating with API Management platforms, adding tracking information, or routing requests through custom gateways.
#### YAML format (recommended for multiple headers)
```yaml
steps:
- name: AI Inference with Azure APIM
id: inference
uses: actions/ai-inference@v1
with:
prompt: 'Analyze this code for security issues...'
endpoint: ${{ secrets.APIM_ENDPOINT }}
token: ${{ secrets.APIM_KEY }}
custom-headers: |
Ocp-Apim-Subscription-Key: ${{ secrets.APIM_SUBSCRIPTION_KEY }}
serviceName: code-review-workflow
env: production
team: security
computer: github-actions
```
#### JSON format (alternative for compact syntax)
```yaml
steps:
- name: AI Inference with Custom Headers
id: inference
uses: actions/ai-inference@v1
with:
prompt: 'Hello!'
custom-headers: '{"X-Custom-Header": "value", "X-Team": "engineering", "X-Request-ID": "${{ github.run_id }}"}'
```
#### Use cases for custom headers
- **API Management**: Integrate with Azure APIM, AWS API Gateway, Kong, or other API management platforms
- **Request tracking**: Add correlation IDs, request IDs, or workflow identifiers
- **Rate limiting**: Include quota or tier information for custom rate limiting
- **Multi-tenancy**: Identify teams, services, or environments
- **Observability**: Add metadata for logging, monitoring, and debugging
- **Routing**: Control request routing through custom gateways or load balancers
**Header name requirements**: Header names must follow the HTTP token syntax defined in RFC 7230 (which permits underscores). For maximum compatibility with intermediaries and tooling, we recommend using only alphanumeric characters and hyphens.
**Security note**: Always use GitHub secrets for sensitive header values like API keys, tokens, or passwords. The action automatically masks common sensitive headers (containing `key`, `token`, `secret`, `password`, or `authorization`) in logs.
### GitHub MCP Integration (Model Context Protocol)
This action now supports **read-only** integration with the GitHub-hosted Model
Context Protocol (MCP) server, which provides access to GitHub tools like
repository management, issue tracking, and pull request operations.
#### Authentication
You can authenticate the MCP server with **either**:
1. **Personal Access Token (PAT)** user-scoped token
2. **GitHub App Installation Token** (`ghs_…`) short-lived, app-scoped token
> The built-in `GITHUB_TOKEN` is **not** accepted by the MCP server.
> Using a **GitHub App installation token** is recommended in most CI environments because it is short-lived and least-privilege by design.
#### Enabling MCP in the action
Set `enable-github-mcp: true` and provide a token via `github-mcp-token`.
```yaml
steps:
- name: AI Inference with GitHub Tools
@@ -170,7 +258,7 @@ steps:
with:
prompt: 'List my open pull requests and create a summary'
enable-github-mcp: true
token: ${{ secrets.USER_PAT }}
token: ${{ secrets.USER_PAT }} # or a ghs_ installation token
```
If you want, you can use separate tokens for the AI inference endpoint
@@ -185,9 +273,28 @@ steps:
prompt: 'List my open pull requests and create a summary'
enable-github-mcp: true
token: ${{ secrets.GITHUB_TOKEN }}
github-mcp-token: ${{ secrets.USER_PAT }}
github-mcp-token: ${{ secrets.USER_PAT }} # or a ghs_ installation token
```
#### Configuring GitHub MCP Toolsets
By default, the GitHub MCP server provides a standard set of tools (`context`, `repos`, `issues`, `pull_requests`, `users`). You can customize which toolsets are available by specifying the `github-mcp-toolsets` parameter:
```yaml
steps:
- name: AI Inference with Custom Toolsets
id: inference
uses: actions/ai-inference@v2
with:
prompt: 'Analyze recent workflow runs and check security alerts'
enable-github-mcp: true
token: ${{ secrets.USER_PAT }}
github-mcp-toolsets: 'repos,issues,pull_requests,actions,code_security'
```
**Available toolsets:**
See: [Tool configuration](https://github.com/github/github-mcp-server/blob/main/README.md#tool-configuration)
When MCP is enabled, the AI model will have access to GitHub tools and can
perform actions like searching issues and PRs.
@@ -196,20 +303,24 @@ perform actions like searching issues and PRs.
Various inputs are defined in [`action.yml`](action.yml) to let you configure
the action:
| Name | Description | Default |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `token` | Token to use for inference. Typically the GITHUB_TOKEN secret | `github.token` |
| `prompt` | The prompt to send to the model | N/A |
| `prompt-file` | Path to a file containing the prompt (supports .txt and .prompt.yml formats). If both `prompt` and `prompt-file` are provided, `prompt-file` takes precedence | `""` |
| `input` | Template variables in YAML format for .prompt.yml files (e.g., `var1: value1` on separate lines) | `""` |
| `file_input` | Template variables in YAML where values are file paths. The file contents are read and used for templating | `""` |
| `system-prompt` | The system prompt to send to the model | `"You are a helpful assistant"` |
| `system-prompt-file` | Path to a file containing the system prompt. If both `system-prompt` and `system-prompt-file` are provided, `system-prompt-file` takes precedence | `""` |
| `model` | The model to use for inference. Must be available in the [GitHub Models](https://github.com/marketplace?type=models) catalog | `openai/gpt-4o` |
| `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 | `""` |
| Name | Description | Default |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ |
| `token` | Token to use for inference. Typically the GITHUB_TOKEN secret | `github.token` |
| `prompt` | The prompt to send to the model | N/A |
| `prompt-file` | Path to a file containing the prompt (supports .txt and .prompt.yml formats). If both `prompt` and `prompt-file` are provided, `prompt-file` takes precedence | `""` |
| `input` | Template variables in YAML format for .prompt.yml files (e.g., `var1: value1` on separate lines) | `""` |
| `file_input` | Template variables in YAML where values are file paths. The file contents are read and used for templating | `""` |
| `system-prompt` | The system prompt to send to the model | `"You are a helpful assistant"` |
| `system-prompt-file` | Path to a file containing the system prompt. If both `system-prompt` and `system-prompt-file` are provided, `system-prompt-file` takes precedence | `""` |
| `model` | The model to use for inference. Must be available in the [GitHub Models](https://github.com/marketplace?type=models) catalog | `openai/gpt-4o` |
| `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 maximum number of tokens to generate (deprecated, use `max-completion-tokens` instead) | 200 |
| `max-completion-tokens` | The maximum number of tokens to generate | `""` |
| `temperature` | The sampling temperature to use (0-1) | `""` |
| `top-p` | The nucleus sampling parameter to use (0-1) | `""` |
| `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). | `""` |
| `custom-headers` | Custom HTTP headers to include in API requests. Supports both YAML format (`header1: value1`) and JSON format (`{"header1": "value1"}`). Useful for API Management platforms, rate limiting, and request tracking. | `""` |
## Outputs

View File

@@ -106,7 +106,10 @@ describe('helpers.ts - inference request building', () => {
undefined,
undefined,
'gpt-4',
undefined,
undefined,
100,
undefined,
'https://api.test.com',
'test-token',
)
@@ -117,7 +120,10 @@ describe('helpers.ts - inference request building', () => {
{role: 'user', content: 'User message'},
],
modelName: 'gpt-4',
temperature: undefined,
topP: undefined,
maxTokens: 100,
maxCompletionTokens: undefined,
endpoint: 'https://api.test.com',
token: 'test-token',
responseFormat: {
@@ -136,7 +142,10 @@ describe('helpers.ts - inference request building', () => {
'System prompt',
'User prompt',
'gpt-4',
undefined,
undefined,
100,
undefined,
'https://api.test.com',
'test-token',
)
@@ -147,7 +156,10 @@ describe('helpers.ts - inference request building', () => {
{role: 'user', content: 'User prompt'},
],
modelName: 'gpt-4',
temperature: undefined,
topP: undefined,
maxTokens: 100,
maxCompletionTokens: undefined,
endpoint: 'https://api.test.com',
token: 'test-token',
responseFormat: undefined,

View File

@@ -11,7 +11,7 @@ vi.mock('fs', () => ({
vi.mock('@actions/core', () => core)
const {loadContentFromFileOrInput} = await import('../src/helpers.js')
const {loadContentFromFileOrInput, parseCustomHeaders} = await import('../src/helpers.js')
describe('helpers.ts', () => {
beforeEach(() => {
@@ -132,4 +132,243 @@ describe('helpers.ts', () => {
expect(result).toBe(defaultValue)
})
})
describe('parseCustomHeaders', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('parses YAML format headers correctly', () => {
const yamlInput = `header1: value1
header2: value2
X-Custom-Header: custom-value`
const result = parseCustomHeaders(yamlInput)
expect(result).toEqual({
header1: 'value1',
header2: 'value2',
'X-Custom-Header': 'custom-value',
})
expect(core.info).toHaveBeenCalledWith('Custom header added: header1: value1')
expect(core.info).toHaveBeenCalledWith('Custom header added: header2: value2')
expect(core.info).toHaveBeenCalledWith('Custom header added: X-Custom-Header: custom-value')
})
it('parses JSON format headers correctly', () => {
const jsonInput = '{"header1": "value1", "header2": "value2", "X-Team": "engineering"}'
const result = parseCustomHeaders(jsonInput)
expect(result).toEqual({
header1: 'value1',
header2: 'value2',
'X-Team': 'engineering',
})
expect(core.info).toHaveBeenCalledWith('Custom header added: header1: value1')
expect(core.info).toHaveBeenCalledWith('Custom header added: header2: value2')
expect(core.info).toHaveBeenCalledWith('Custom header added: X-Team: engineering')
})
it('returns empty object for empty input', () => {
expect(parseCustomHeaders('')).toEqual({})
expect(parseCustomHeaders(' ')).toEqual({})
expect(core.warning).not.toHaveBeenCalled()
})
it('masks sensitive header values in logs', () => {
const yamlInput = `Ocp-Apim-Subscription-Key: secret123
X-Api-Token: token456
Authorization: Bearer abc123
serviceName: my-service
password: pass123`
const result = parseCustomHeaders(yamlInput)
expect(result).toEqual({
'Ocp-Apim-Subscription-Key': 'secret123',
'X-Api-Token': 'token456',
Authorization: 'Bearer abc123',
serviceName: 'my-service',
password: 'pass123',
})
// Sensitive headers should be masked
expect(core.info).toHaveBeenCalledWith('Custom header added: Ocp-Apim-Subscription-Key: ***MASKED***')
expect(core.info).toHaveBeenCalledWith('Custom header added: X-Api-Token: ***MASKED***')
expect(core.info).toHaveBeenCalledWith('Custom header added: Authorization: ***MASKED***')
expect(core.info).toHaveBeenCalledWith('Custom header added: password: ***MASKED***')
// Non-sensitive headers should not be masked
expect(core.info).toHaveBeenCalledWith('Custom header added: serviceName: my-service')
})
it('validates header names and skips invalid ones', () => {
const yamlInput = `valid-header: value1
invalid header: value2
invalid_underscore: value3
invalid@header: value4
valid123: value5`
const result = parseCustomHeaders(yamlInput)
expect(result).toEqual({
'valid-header': 'value1',
valid123: 'value5',
})
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining('Skipping invalid header name: invalid header'))
expect(core.warning).toHaveBeenCalledWith(
expect.stringContaining('Skipping invalid header name: invalid_underscore'),
)
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining('Skipping invalid header name: invalid@header'))
})
it('warns and returns empty object for invalid JSON', () => {
const invalidJson = '{invalid json}'
const result = parseCustomHeaders(invalidJson)
expect(result).toEqual({})
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining('Failed to parse custom headers'))
})
it('warns and returns empty object for invalid YAML', () => {
const invalidYaml = 'invalid: yaml: structure: bad'
const result = parseCustomHeaders(invalidYaml)
expect(result).toEqual({})
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining('Failed to parse custom headers'))
})
it('warns and returns empty object for JSON array', () => {
const jsonArray = '["header1", "header2"]'
const result = parseCustomHeaders(jsonArray)
expect(result).toEqual({})
expect(core.warning).toHaveBeenCalledWith('Custom headers JSON must be an object, not null or an array')
})
it('warns and returns empty object for null value', () => {
// The string 'null' is valid YAML and gets parsed as null
const nullValue = 'null'
const result = parseCustomHeaders(nullValue)
expect(result).toEqual({})
expect(core.warning).toHaveBeenCalledWith('Custom headers YAML must be an object')
})
it('warns and returns empty object for YAML array', () => {
const yamlArray = `- header1
- header2`
const result = parseCustomHeaders(yamlArray)
expect(result).toEqual({})
expect(core.warning).toHaveBeenCalledWith('Custom headers YAML must be an object')
})
it('converts non-string values to strings', () => {
const jsonInput = '{"numericHeader": 123, "boolHeader": true, "nullHeader": null}'
const result = parseCustomHeaders(jsonInput)
expect(result).toEqual({
numericHeader: '123',
boolHeader: 'true',
nullHeader: 'null',
})
})
it('rejects header values with newline characters (LF)', () => {
const jsonInput = '{"X-Custom-Header": "value\\nwith\\nnewline", "header1": "safe-value"}'
const result = parseCustomHeaders(jsonInput)
// Only the safe header should be accepted
expect(result).toEqual({
header1: 'safe-value',
})
expect(core.warning).toHaveBeenCalledWith(
'Skipping header "X-Custom-Header" because its value contains newline characters, which are not allowed in HTTP header values.',
)
})
it('rejects header values with carriage return characters (CR)', () => {
const jsonInput = '{"X-Injected": "value\\rwith\\rcarriage", "X-Safe": "safe-value"}'
const result = parseCustomHeaders(jsonInput)
// Only the safe header should be accepted
expect(result).toEqual({
'X-Safe': 'safe-value',
})
expect(core.warning).toHaveBeenCalledWith(
'Skipping header "X-Injected" because its value contains newline characters, which are not allowed in HTTP header values.',
)
})
it('rejects header values with CRLF sequences', () => {
const jsonInput = '{"X-Attack": "value\\r\\nInjected-Header: malicious", "X-Valid": "normal"}'
const result = parseCustomHeaders(jsonInput)
// Only the valid header should be accepted
expect(result).toEqual({
'X-Valid': 'normal',
})
expect(core.warning).toHaveBeenCalledWith(
'Skipping header "X-Attack" because its value contains newline characters, which are not allowed in HTTP header values.',
)
})
it('rejects multiline YAML values for security', () => {
const yamlInput = `header1: value1
header2: |
multiline
value
here`
const result = parseCustomHeaders(yamlInput)
// header2 should be rejected because it contains newlines
expect(result).toEqual({
header1: 'value1',
})
expect(core.warning).toHaveBeenCalledWith(
'Skipping header "header2" because its value contains newline characters, which are not allowed in HTTP header values.',
)
})
it('handles complex real-world Azure APIM example', () => {
const apimHeaders = `Ocp-Apim-Subscription-Key: my-subscription-key-123
serviceName: terraform-plan-workflow
env: prod
team: infrastructure
computer: github-actions
systemID: terraform-ci`
const result = parseCustomHeaders(apimHeaders)
expect(result).toEqual({
'Ocp-Apim-Subscription-Key': 'my-subscription-key-123',
serviceName: 'terraform-plan-workflow',
env: 'prod',
team: 'infrastructure',
computer: 'github-actions',
systemID: 'terraform-ci',
})
// Only the subscription key should be masked
expect(core.info).toHaveBeenCalledWith('Custom header added: Ocp-Apim-Subscription-Key: ***MASKED***')
expect(core.info).toHaveBeenCalledWith('Custom header added: serviceName: terraform-plan-workflow')
})
})
})

View File

@@ -31,7 +31,7 @@ describe('inference.ts', () => {
{role: 'user' as const, content: 'Hello, AI!'},
],
modelName: 'gpt-4',
maxTokens: 100,
maxCompletionTokens: 100,
endpoint: 'https://api.test.com',
token: 'test-token',
}
@@ -72,9 +72,52 @@ describe('inference.ts', () => {
content: 'Hello, AI!',
},
],
max_tokens: 100,
max_completion_tokens: 100,
model: 'gpt-4',
})
// Verify OpenAI client was initialized with empty custom headers
expect(mockOpenAIClient).toHaveBeenCalledWith({
apiKey: 'test-token',
baseURL: 'https://api.test.com',
defaultHeaders: {},
})
})
it('includes custom headers in OpenAI client', async () => {
const requestWithHeaders = {
...mockRequest,
customHeaders: {
'X-Custom-Header': 'custom-value',
'Ocp-Apim-Subscription-Key': 'secret123',
},
}
const mockResponse = {
choices: [
{
message: {
content: 'Response with headers',
},
},
],
}
mockCreate.mockResolvedValue(mockResponse)
const result = await simpleInference(requestWithHeaders)
expect(result).toBe('Response with headers')
// Verify OpenAI client was initialized with custom headers
expect(mockOpenAIClient).toHaveBeenCalledWith({
apiKey: 'test-token',
baseURL: 'https://api.test.com',
defaultHeaders: {
'X-Custom-Header': 'custom-value',
'Ocp-Apim-Subscription-Key': 'secret123',
},
})
})
it('handles null response content', async () => {
@@ -133,7 +176,7 @@ describe('inference.ts', () => {
content: 'Hello, AI!',
},
],
max_tokens: 100,
max_completion_tokens: 100,
model: 'gpt-4',
response_format: requestWithResponseFormat.responseFormat,
})
@@ -185,7 +228,51 @@ describe('inference.ts', () => {
expect(callArgs.tools).toEqual(mockMcpClient.tools)
expect(callArgs.response_format).toBeUndefined()
expect(callArgs.model).toBe('gpt-4')
expect(callArgs.max_tokens).toBe(100)
expect(callArgs.max_completion_tokens).toBe(100)
// Verify OpenAI client was initialized with empty custom headers
expect(mockOpenAIClient).toHaveBeenCalledWith({
apiKey: 'test-token',
baseURL: 'https://api.test.com',
defaultHeaders: {},
})
})
it('includes custom headers in MCP inference', async () => {
const requestWithHeaders = {
...mockRequest,
customHeaders: {
serviceName: 'test-service',
'X-Team': 'engineering',
},
}
const mockResponse = {
choices: [
{
message: {
content: 'MCP response with headers',
tool_calls: null,
},
},
],
}
mockCreate.mockResolvedValue(mockResponse)
const result = await mcpInference(requestWithHeaders, mockMcpClient)
expect(result).toBe('MCP response with headers')
// Verify OpenAI client was initialized with custom headers
expect(mockOpenAIClient).toHaveBeenCalledWith({
apiKey: 'test-token',
baseURL: 'https://api.test.com',
defaultHeaders: {
serviceName: 'test-service',
'X-Team': 'engineering',
},
})
})
it('executes tool calls and continues conversation', async () => {
@@ -546,4 +633,64 @@ describe('inference.ts', () => {
expect(result).toBe('{"immediate": "result"}')
})
})
describe('token param routing', () => {
it('sends max_tokens when only maxTokens is set', async () => {
const requestWithMaxTokens = {
...mockRequest,
maxCompletionTokens: undefined,
maxTokens: 100,
}
const mockResponse = {
choices: [
{
message: {
content: 'Direct max_tokens response',
},
},
],
}
mockCreate.mockResolvedValueOnce(mockResponse)
const result = await simpleInference(requestWithMaxTokens)
expect(result).toBe('Direct max_tokens response')
expect(mockCreate).toHaveBeenCalledTimes(1)
// Should have sent max_tokens directly
expect(mockCreate.mock.calls[0][0]).toHaveProperty('max_tokens', 100)
expect(mockCreate.mock.calls[0][0]).not.toHaveProperty('max_completion_tokens')
})
it('sends neither token param when both are undefined', async () => {
const requestWithNoTokens = {
...mockRequest,
maxCompletionTokens: undefined,
maxTokens: undefined,
}
const mockResponse = {
choices: [
{
message: {
content: 'No token limit response',
},
},
],
}
mockCreate.mockResolvedValueOnce(mockResponse)
const result = await simpleInference(requestWithNoTokens)
expect(result).toBe('No token limit response')
expect(mockCreate).toHaveBeenCalledTimes(1)
const params = mockCreate.mock.calls[0][0]
expect(params).not.toHaveProperty('max_tokens')
expect(params).not.toHaveProperty('max_completion_tokens')
})
})
})

View File

@@ -66,7 +66,7 @@ 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-'))
}
vi.mock('fs', () => ({
@@ -75,6 +75,15 @@ vi.mock('fs', () => ({
writeFileSync: mockWriteFileSync,
}))
// Mocks for tmp module to control temporary file creation
const mockFileSync = vi.fn().mockReturnValue({
name: '/secure/temp/dir/modelResponse-abc123.txt',
})
vi.mock('tmp', () => ({
fileSync: mockFileSync,
}))
// Mock MCP and inference modules
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockConnectToGitHubMCP = vi.fn() as MockedFunction<any>
@@ -159,9 +168,13 @@ describe('main.ts', () => {
],
modelName: 'gpt-4',
maxTokens: 100,
maxCompletionTokens: undefined,
endpoint: 'https://api.test.com',
token: 'fake-token',
responseFormat: undefined,
temperature: undefined,
topP: undefined,
customHeaders: {},
})
expect(mockConnectToGitHubMCP).not.toHaveBeenCalled()
expect(mockMcpInference).not.toHaveBeenCalled()
@@ -186,7 +199,7 @@ describe('main.ts', () => {
await run()
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token')
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token', '')
expect(mockMcpInference).toHaveBeenCalledWith(
expect.objectContaining({
messages: [
@@ -213,7 +226,7 @@ describe('main.ts', () => {
await run()
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token')
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token', '')
expect(mockSimpleInference).toHaveBeenCalled()
expect(mockMcpInference).not.toHaveBeenCalled()
expect(core.warning).toHaveBeenCalledWith('MCP connection failed, falling back to simple inference')
@@ -247,9 +260,13 @@ describe('main.ts', () => {
],
modelName: 'gpt-4',
maxTokens: 100,
maxCompletionTokens: undefined,
endpoint: 'https://api.test.com',
token: 'fake-token',
responseFormat: undefined,
temperature: undefined,
topP: undefined,
customHeaders: {},
})
verifyStandardResponse()
expect(mockProcessExit).toHaveBeenCalledWith(0)
@@ -269,4 +286,25 @@ describe('main.ts', () => {
expect(core.setFailed).toHaveBeenCalledWith(`File for prompt-file was not found: ${promptFile}`)
expect(mockProcessExit).toHaveBeenCalledWith(1)
})
it('creates temporary files that persist for downstream steps', async () => {
mockInputs({
prompt: 'Test prompt',
'system-prompt': 'You are a test assistant.',
})
await run()
// Verify temp file is created with keep: true so it persists
expect(mockFileSync).toHaveBeenCalledWith({
prefix: 'modelResponse-',
postfix: '.txt',
keep: true,
})
expect(core.setOutput).toHaveBeenNthCalledWith(2, 'response-file', '/secure/temp/dir/modelResponse-abc123.txt')
expect(mockWriteFileSync).toHaveBeenCalledWith('/secure/temp/dir/modelResponse-abc123.txt', 'Hello, user!', 'utf-8')
expect(mockProcessExit).toHaveBeenCalledWith(0)
})
})

View File

@@ -113,6 +113,40 @@ describe('mcp.ts', () => {
expect(result?.tools).toHaveLength(0)
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
})
it('uses default toolsets when toolsets parameter is not provided', async () => {
const token = 'test-token'
mockConnect.mockResolvedValue(undefined)
mockListTools.mockResolvedValue({tools: []})
await connectToGitHubMCP(token)
expect(core.info).toHaveBeenCalledWith('Using default GitHub MCP toolsets')
})
it('uses custom toolsets when toolsets parameter is provided', async () => {
const token = 'test-token'
const toolsets = 'repos,issues,pull_requests,actions'
mockConnect.mockResolvedValue(undefined)
mockListTools.mockResolvedValue({tools: []})
await connectToGitHubMCP(token, toolsets)
expect(core.info).toHaveBeenCalledWith('Using GitHub MCP toolsets: repos,issues,pull_requests,actions')
})
it('ignores empty toolsets parameter', async () => {
const token = 'test-token'
mockConnect.mockResolvedValue(undefined)
mockListTools.mockResolvedValue({tools: []})
await connectToGitHubMCP(token, ' ')
expect(core.info).toHaveBeenCalledWith('Using default GitHub MCP toolsets')
})
})
describe('executeToolCall', () => {

View File

@@ -43,9 +43,21 @@ inputs:
required: false
default: ''
max-tokens:
description: The maximum number of tokens to generate
description: The maximum number of tokens to generate (deprecated)
required: false
default: '200'
max-completion-tokens:
description: The maximum number of tokens to generate
required: false
default: ''
temperature:
description: The sampling temperature to use (0-1)
required: false
default: ''
top-p:
description: The nucleus sampling parameter to use (0-1)
required: false
default: ''
token:
description: The token to use
required: false
@@ -55,7 +67,15 @@ inputs:
required: false
default: 'false'
github-mcp-token:
description: The token to use for GitHub MCP server (defaults to GITHUB_TOKEN if not specified)
description: The token to use for GitHub MCP server (defaults to the main token if not specified). This must be a PAT for MCP to work.
required: false
default: ''
github-mcp-toolsets:
description: 'Comma-separated list of toolsets to enable for GitHub MCP (e.g., "repos,issues,pull_requests,actions"). Use "all" for all toolsets, "default" for default set. If not specified, uses default toolsets (context,repos,issues,pull_requests,users).'
required: false
default: ''
custom-headers:
description: 'Custom HTTP headers to include in API requests. Supports both YAML format (header1: value1) and JSON format ({"header1": "value1"}). Useful for API Management platforms, rate limiting, and request tracking.'
required: false
default: ''
@@ -67,5 +87,5 @@ outputs:
description: The file path where the response is saved
runs:
using: node20
using: node24
main: dist/index.js

34187
dist/index.js generated vendored

File diff suppressed because it is too large Load Diff

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -19,7 +19,7 @@ const compat = new FlatCompat({
export default [
{
ignores: ['**/coverage', '**/dist', '**/linter', '**/node_modules'],
ignores: ['**/coverage', '**/dist', '**/linter', '**/node_modules', 'script/**'],
},
...compat.extends(
'eslint:recommended',

355
package-lock.json generated
View File

@@ -10,10 +10,13 @@
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@modelcontextprotocol/sdk": "^1.15.1",
"js-yaml": "^4.1.0",
"@modelcontextprotocol/sdk": "^1.25.2",
"@rollup/rollup-linux-x64-gnu": "4.57.1",
"@types/tmp": "^0.2.6",
"js-yaml": "^4.1.1",
"openai": "^5.11.0",
"pkce-challenge": "^5.0.0"
"pkce-challenge": "^5.0.0",
"tmp": "^0.2.4"
},
"devDependencies": {
"@eslint/compat": "^1.3.0",
@@ -24,7 +27,7 @@
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.2",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.15.31",
"@types/node": "^24.1.0",
"@typescript-eslint/eslint-plugin": "^8.34.0",
"@typescript-eslint/parser": "^8.32.1",
"eslint": "^9.29.0",
@@ -39,7 +42,7 @@
"vitest": "^3"
},
"engines": {
"node": ">=20"
"node": ">=24"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "*"
@@ -1436,6 +1439,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/@hono/node-server": {
"version": "1.19.7",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.7.tgz",
"integrity": "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==",
"license": "MIT",
"engines": {
"node": ">=18.14.1"
},
"peerDependencies": {
"hono": "^4"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1589,12 +1604,14 @@
"license": "MIT"
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.15.1.tgz",
"integrity": "sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w==",
"version": "1.25.2",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz",
"integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==",
"license": "MIT",
"dependencies": {
"ajv": "^6.12.6",
"@hono/node-server": "^1.19.7",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"content-type": "^1.0.5",
"cors": "^2.8.5",
"cross-spawn": "^7.0.5",
@@ -1602,15 +1619,51 @@
"eventsource-parser": "^3.0.0",
"express": "^5.0.1",
"express-rate-limit": "^7.5.0",
"jose": "^6.1.1",
"json-schema-typed": "^8.0.2",
"pkce-challenge": "^5.0.0",
"raw-body": "^3.0.0",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.24.1"
"zod": "^3.25 || ^4.0",
"zod-to-json-schema": "^3.25.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@cfworker/json-schema": "^4.1.1",
"zod": "^3.25 || ^4.0"
},
"peerDependenciesMeta": {
"@cfworker/json-schema": {
"optional": true
},
"zod": {
"optional": false
}
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz",
@@ -2337,9 +2390,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.0.tgz",
"integrity": "sha512-Cv+moII5C8RM6gZbR3cb21o6rquVDZrN2o81maROg1LFzBz2dZUwIQSxFA8GtGZ/F2KtsqQ2z3eFPBb6akvQNg==",
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
"integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
"cpu": [
"x64"
],
@@ -2476,13 +2529,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.15.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz",
"integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==",
"version": "24.1.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
"undici-types": "~7.8.0"
}
},
"node_modules/@types/resolve": {
@@ -2492,6 +2545,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/tmp": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz",
"integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.34.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz",
@@ -3205,6 +3264,7 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
@@ -3217,6 +3277,45 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
@@ -3532,23 +3631,27 @@
}
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
"license": "MIT",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"debug": "^4.4.3",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
"raw-body": "^3.0.1",
"type-is": "^2.0.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/bottleneck": {
@@ -3998,9 +4101,9 @@
}
},
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -4993,18 +5096,19 @@
}
},
"node_modules/express": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"body-parser": "^2.2.1",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
@@ -5120,6 +5224,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
@@ -5129,6 +5234,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/fast-xml-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.3.tgz",
@@ -5159,11 +5280,14 @@
}
},
"node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -5449,9 +5573,9 @@
}
},
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -5694,29 +5818,34 @@
"node": ">= 0.4"
}
},
"node_modules/hono": {
"version": "4.11.3",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz",
"integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=16.9.0"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/http-errors/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/http-proxy-agent": {
@@ -5748,15 +5877,19 @@
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/ieee754": {
@@ -6352,6 +6485,15 @@
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jose": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz",
"integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-tokens": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
@@ -6360,9 +6502,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -6382,8 +6524,15 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-typed": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
"integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
"license": "BSD-2-Clause"
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
@@ -6505,9 +6654,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"dev": true,
"license": "MIT"
},
@@ -7837,15 +7986,16 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -7902,18 +8052,18 @@
}
},
"node_modules/raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
"node": ">= 0.10"
}
},
"node_modules/react-is": {
@@ -8017,6 +8167,15 @@
"node": ">=0.10"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
@@ -8897,14 +9056,14 @@
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -8943,6 +9102,15 @@
"node": ">=14.0.0"
}
},
"node_modules/tmp": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"license": "MIT",
"engines": {
"node": ">=14.14"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -9207,9 +9375,9 @@
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"dev": true,
"license": "MIT"
},
@@ -9279,6 +9447,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
@@ -9301,18 +9470,18 @@
}
},
"node_modules/vite": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
"version": "7.1.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz",
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.6",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.40.0",
"tinyglobby": "^0.2.14"
"rollup": "^4.43.0",
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
@@ -9848,12 +10017,12 @@
}
},
"node_modules/zod-to-json-schema": {
"version": "3.24.6",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
"version": "3.25.0",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz",
"integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==",
"license": "ISC",
"peerDependencies": {
"zod": "^3.24.1"
"zod": "^3.25 || ^4"
}
}
}

View File

@@ -6,7 +6,7 @@
".": "./dist/index.js"
},
"engines": {
"node": ">=20"
"node": ">=24"
},
"scripts": {
"bundle": "npm run format:write && npm run package",
@@ -24,10 +24,12 @@
"prettier": "@github/prettier-config",
"dependencies": {
"@actions/core": "^1.11.1",
"@modelcontextprotocol/sdk": "^1.15.1",
"js-yaml": "^4.1.0",
"@modelcontextprotocol/sdk": "^1.25.2",
"@types/tmp": "^0.2.6",
"js-yaml": "^4.1.1",
"openai": "^5.11.0",
"pkce-challenge": "^5.0.0"
"pkce-challenge": "^5.0.0",
"tmp": "^0.2.4"
},
"devDependencies": {
"@eslint/compat": "^1.3.0",
@@ -38,7 +40,7 @@
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.2",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.15.31",
"@types/node": "^24.1.0",
"@typescript-eslint/eslint-plugin": "^8.34.0",
"@typescript-eslint/parser": "^8.32.1",
"eslint": "^9.29.0",

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env node
/**
* A simple mock OpenAI-compatible inference server for CI testing.
* This returns predictable responses without needing real API credentials.
*/
import http from 'http'
const PORT = process.env.MOCK_SERVER_PORT || 3456
const server = http.createServer((req, res) => {
let body = ''
req.on('data', chunk => {
body += chunk.toString()
})
req.on('end', () => {
console.log(`[Mock Server] ${req.method} ${req.url}`)
// Handle chat completions endpoint
if (req.url === '/chat/completions' && req.method === 'POST') {
const request = JSON.parse(body)
const userMessage = request.messages?.find(m => m.role === 'user')?.content || 'No prompt'
const response = {
id: 'mock-completion-id',
object: 'chat.completion',
created: Date.now(),
model: request.model || 'mock-model',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: `Mock response to: "${userMessage.slice(0, 50)}..."`,
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
},
}
res.writeHead(200, {'Content-Type': 'application/json'})
res.end(JSON.stringify(response))
return
}
// Health check endpoint
if (req.url === '/health' || req.url === '/') {
res.writeHead(200, {'Content-Type': 'application/json'})
res.end(JSON.stringify({status: 'ok'}))
return
}
// 404 for unknown routes
res.writeHead(404, {'Content-Type': 'application/json'})
res.end(JSON.stringify({error: 'Not found'}))
})
})
server.listen(PORT, () => {
console.log(`[Mock Server] Listening on http://localhost:${PORT}`)
console.log('[Mock Server] Endpoints:')
console.log(' POST /chat/completions - Mock chat completion')
console.log(' GET /health - Health check')
})

View File

@@ -1,5 +1,6 @@
import * as core from '@actions/core'
import * as fs from 'fs'
import * as yaml from 'js-yaml'
import {PromptConfig} from './prompt.js'
import {InferenceRequest} from './inference.js'
@@ -74,6 +75,83 @@ export function buildResponseFormat(
return undefined
}
/**
* Parse custom headers from YAML or JSON format
* @param input - String in YAML or JSON format containing headers
* @returns Record of header names to values, or empty object if invalid
*/
export function parseCustomHeaders(input: string): Record<string, string> {
if (!input || input.trim() === '') {
return {}
}
const trimmedInput = input.trim()
try {
// Try JSON first (check if it starts with { or [)
if (trimmedInput.startsWith('{') || trimmedInput.startsWith('[')) {
const parsed = JSON.parse(trimmedInput)
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
core.warning('Custom headers JSON must be an object, not null or an array')
return {}
}
return validateAndMaskHeaders(parsed as Record<string, unknown>)
}
// Try YAML
const parsed = yaml.load(trimmedInput)
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
core.warning('Custom headers YAML must be an object')
return {}
}
return validateAndMaskHeaders(parsed as Record<string, unknown>)
} catch (error) {
core.warning(`Failed to parse custom headers: ${error instanceof Error ? error.message : 'Unknown error'}`)
return {}
}
}
/**
* Validate header names and mask sensitive values in logs
* @param headers - Raw headers object
* @returns Validated headers with string values
*/
function validateAndMaskHeaders(headers: Record<string, unknown>): Record<string, string> {
const validHeaders: Record<string, string> = {}
const sensitivePatterns = ['key', 'token', 'secret', 'password', 'authorization']
for (const [name, value] of Object.entries(headers)) {
// Validate header name (basic HTTP header name validation, RFC 7230: letters, digits, and hyphens)
if (!/^[A-Za-z0-9-]+$/.test(name)) {
core.warning(`Skipping invalid header name: ${name} (only alphanumeric characters and hyphens allowed)`)
continue
}
// Convert value to string
const stringValue = String(value)
// Validate header value to prevent CRLF/header injection
if (stringValue.includes('\r') || stringValue.includes('\n')) {
core.warning(
`Skipping header "${name}" because its value contains newline characters, which are not allowed in HTTP header values.`,
)
continue
}
validHeaders[name] = stringValue
// Mask sensitive headers in logs
const lowerName = name.toLowerCase()
const isSensitive = sensitivePatterns.some(pattern => lowerName.includes(pattern))
if (isSensitive) {
core.info(`Custom header added: ${name}: ***MASKED***`)
} else {
core.info(`Custom header added: ${name}: ${stringValue}`)
}
}
return validHeaders
}
/**
* Build complete InferenceRequest from prompt config and inputs
*/
@@ -82,9 +160,13 @@ export function buildInferenceRequest(
systemPrompt: string | undefined,
prompt: string | undefined,
modelName: string,
maxTokens: number,
temperature: number | undefined,
topP: number | undefined,
maxTokens: number | undefined, // Deprecated
maxCompletionTokens: number | undefined,
endpoint: string,
token: string,
customHeaders?: Record<string, string>,
): InferenceRequest {
const messages = buildMessages(promptConfig, systemPrompt, prompt)
const responseFormat = buildResponseFormat(promptConfig)
@@ -92,9 +174,13 @@ export function buildInferenceRequest(
return {
messages,
modelName,
maxTokens,
temperature,
topP,
maxTokens, // Deprecated
maxCompletionTokens,
endpoint,
token,
responseFormat,
customHeaders,
}
}

View File

@@ -12,10 +12,14 @@ interface ChatMessage {
export interface InferenceRequest {
messages: Array<{role: 'system' | 'user' | 'assistant' | 'tool'; content: string}>
modelName: string
maxTokens: number
maxTokens?: number // Deprecated
maxCompletionTokens?: number
endpoint: string
token: string
temperature?: number
topP?: number
responseFormat?: {type: 'json_schema'; json_schema: unknown} // Processed response format for the API
customHeaders?: Record<string, string> // Custom HTTP headers to include in API requests
}
export interface InferenceResponse {
@@ -30,6 +34,20 @@ export interface InferenceResponse {
}>
}
/**
* Build according to what input was passed, default to max_tokens.
* Only one of max_tokens or max_completion_tokens will be set.
*/
function buildMaxTokensParam(request: InferenceRequest): {max_tokens?: number; max_completion_tokens?: number} {
if (request.maxCompletionTokens != null) {
return {max_completion_tokens: request.maxCompletionTokens}
}
if (request.maxTokens != null) {
return {max_tokens: request.maxTokens}
}
return {}
}
/**
* Simple one-shot inference without tools
*/
@@ -39,12 +57,15 @@ export async function simpleInference(request: InferenceRequest): Promise<string
const client = new OpenAI({
apiKey: request.token,
baseURL: request.endpoint,
defaultHeaders: request.customHeaders || {},
})
const chatCompletionRequest: OpenAI.Chat.Completions.ChatCompletionCreateParams = {
messages: request.messages as OpenAI.Chat.Completions.ChatCompletionMessageParam[],
max_tokens: request.maxTokens,
model: request.modelName,
temperature: request.temperature,
top_p: request.topP,
...buildMaxTokensParam(request), // Note: solution around models using different underlying max tokens properties
}
// Add response format if specified
@@ -53,21 +74,10 @@ export async function simpleInference(request: InferenceRequest): Promise<string
chatCompletionRequest.response_format = request.responseFormat as any
}
try {
const response = await client.chat.completions.create(chatCompletionRequest)
if ('choices' in response) {
const modelResponse = response.choices[0]?.message?.content
core.info(`Model response: ${modelResponse || 'No response content'}`)
return modelResponse || null
} else {
core.error(`Unexpected response format from API: ${JSON.stringify(response)}`)
return null
}
} catch (error) {
core.error(`API error: ${error}`)
throw error
}
const response = await chatCompletion(client, chatCompletionRequest, 'simpleInference')
const modelResponse = response.choices[0]?.message?.content
core.info(`Model response: ${modelResponse || 'No response content'}`)
return modelResponse || null
}
/**
@@ -82,6 +92,7 @@ export async function mcpInference(
const client = new OpenAI({
apiKey: request.token,
baseURL: request.endpoint,
defaultHeaders: request.customHeaders || {},
})
// Start with the pre-processed messages
@@ -99,8 +110,10 @@ export async function mcpInference(
const chatCompletionRequest: OpenAI.Chat.Completions.ChatCompletionCreateParams = {
messages: messages as OpenAI.Chat.Completions.ChatCompletionMessageParam[],
max_tokens: request.maxTokens,
model: request.modelName,
temperature: request.temperature,
top_p: request.topP,
...buildMaxTokensParam(request), // Note: solution around models using different underlying max tokens properties
}
// Add response format if specified (only on final iteration to avoid conflicts with tool calls)
@@ -112,11 +125,7 @@ export async function mcpInference(
}
try {
const response = await client.chat.completions.create(chatCompletionRequest)
if (!('choices' in response)) {
throw new Error(`Unexpected response format from API: ${JSON.stringify(response)}`)
}
const response = await chatCompletion(client, chatCompletionRequest, `mcpInference iteration ${iterationCount}`)
const assistantMessage = response.choices[0]?.message
const modelResponse = assistantMessage?.content
@@ -133,20 +142,13 @@ export async function mcpInference(
if (!toolCalls || toolCalls.length === 0) {
core.info('No tool calls requested, ending GitHub MCP inference loop')
// If we have a response format set and we haven't explicitly run one final message iteration,
// do another loop with the response format set
if (request.responseFormat && !finalMessage) {
core.info('Making one more MCP loop with the requested response format...')
// Add a user message requesting JSON format and try again
messages.push({
role: 'user',
content: `Please provide your response in the exact ${request.responseFormat.type} format specified.`,
})
finalMessage = true
// Continue the loop to get a properly formatted response
continue
} else {
return modelResponse || null
@@ -154,13 +156,8 @@ 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 as ToolCall[])
// Add tool results to the conversation
messages.push(...toolResults)
core.info('Tool results added, continuing conversation...')
} catch (error) {
core.error(`OpenAI API error: ${error}`)
@@ -178,3 +175,43 @@ export async function mcpInference(
return lastAssistantMessage?.content || null
}
/**
* Wrapper around OpenAI chat.completions.create with defensive handling for cases where
* the SDK returns a raw string (e.g., unexpected content-type or streaming body) instead of
* a parsed object. Ensures an object with a 'choices' array is returned or throws a descriptive error.
*/
async function chatCompletion(
client: OpenAI,
params: OpenAI.Chat.Completions.ChatCompletionCreateParams,
context: string,
): Promise<OpenAI.Chat.Completions.ChatCompletion> {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let response: any = await client.chat.completions.create(params)
core.debug(`${context}: raw response typeof=${typeof response}`)
if (typeof response === 'string') {
// Attempt to parse if we unexpectedly received a string
try {
response = JSON.parse(response)
} catch (e) {
const preview = response.slice(0, 400)
throw new Error(
`${context}: Chat completion response was a string and not valid JSON (${(e as Error).message}). Preview: ${preview}`,
)
}
}
if (!response || typeof response !== 'object' || !('choices' in response)) {
const preview = JSON.stringify(response)?.slice(0, 800)
throw new Error(`${context}: Unexpected response shape (no choices). Preview: ${preview}`)
}
return response as OpenAI.Chat.Completions.ChatCompletion
} catch (err) {
// Re-throw after logging for upstream handling
core.error(`${context}: chatCompletion failed: ${err}`)
throw err
}
}

View File

@@ -1,10 +1,9 @@
import * as core from '@actions/core'
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import * as tmp from 'tmp'
import {connectToGitHubMCP} from './mcp.js'
import {simpleInference, mcpInference} from './inference.js'
import {loadContentFromFileOrInput, buildInferenceRequest} from './helpers.js'
import {loadContentFromFileOrInput, buildInferenceRequest, parseCustomHeaders} from './helpers.js'
import {
loadPromptFile,
parseTemplateVariables,
@@ -13,8 +12,6 @@ import {
parseFileTemplateVariables,
} from './prompt.js'
const RESPONSE_FILE = 'modelResponse.txt'
/**
* The main function for the action.
*
@@ -51,7 +48,14 @@ export async function run(): Promise<void> {
// Get common parameters
const modelName = promptConfig?.model || core.getInput('model')
const maxTokens = parseInt(core.getInput('max-tokens'), 10)
// Parse token limit inputs
const maxCompletionTokensInput =
promptConfig?.modelParameters?.maxCompletionTokens ?? core.getInput('max-completion-tokens')
const maxCompletionTokens = maxCompletionTokensInput ? Number(maxCompletionTokensInput) : undefined
const maxTokensInput = promptConfig?.modelParameters?.maxTokens ?? core.getInput('max-tokens')
const maxTokens = maxCompletionTokens != null ? undefined : maxTokensInput ? Number(maxTokensInput) : undefined
const token = process.env['GITHUB_TOKEN'] || core.getInput('token')
if (token === undefined) {
@@ -60,18 +64,34 @@ export async function run(): Promise<void> {
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
const githubMcpToken = core.getInput('github-mcp-token') || token
const githubMcpToolsets = core.getInput('github-mcp-toolsets')
const endpoint = core.getInput('endpoint')
// Get temperature and topP (prompt YAML modelParameters takes precedence over action inputs)
const temperatureInput = core.getInput('temperature')
const topPInput = core.getInput('top-p')
const temperature =
promptConfig?.modelParameters?.temperature ?? (temperatureInput ? parseFloat(temperatureInput) : undefined)
const topP = promptConfig?.modelParameters?.topP ?? (topPInput ? parseFloat(topPInput) : undefined)
// Parse custom headers
const customHeadersInput = core.getInput('custom-headers')
const customHeaders = parseCustomHeaders(customHeadersInput)
// Build the inference request with pre-processed messages and response format
const inferenceRequest = buildInferenceRequest(
promptConfig,
systemPrompt,
prompt,
modelName,
temperature,
topP,
maxTokens,
maxCompletionTokens,
endpoint,
token,
customHeaders,
)
const enableMcp = core.getBooleanInput('enable-github-mcp') || false
@@ -79,7 +99,7 @@ export async function run(): Promise<void> {
let modelResponse: string | null = null
if (enableMcp) {
const mcpClient = await connectToGitHubMCP(githubMcpToken)
const mcpClient = await connectToGitHubMCP(githubMcpToken, githubMcpToolsets)
if (mcpClient) {
modelResponse = await mcpInference(inferenceRequest, mcpClient)
@@ -93,11 +113,19 @@ export async function run(): Promise<void> {
core.setOutput('response', modelResponse || '')
const responseFilePath = path.join(tempDir(), RESPONSE_FILE)
core.setOutput('response-file', responseFilePath)
// Create a temporary file for the response that persists for downstream steps.
// We use keep: true to prevent automatic cleanup - the file will be cleaned up
// by the runner when the job completes.
const responseFile = tmp.fileSync({
prefix: 'modelResponse-',
postfix: '.txt',
keep: true,
})
core.setOutput('response-file', responseFile.name)
if (modelResponse && modelResponse !== '') {
fs.writeFileSync(responseFilePath, modelResponse, 'utf-8')
fs.writeFileSync(responseFile.name, modelResponse, 'utf-8')
}
} catch (error) {
if (error instanceof Error) {
@@ -112,8 +140,3 @@ export async function run(): Promise<void> {
// Force exit to prevent hanging on open connections
process.exit(0)
}
function tempDir(): string {
const tempDirectory = process.env['RUNNER_TEMP'] || os.tmpdir()
return tempDirectory
}

View File

@@ -35,17 +35,27 @@ 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, toolsets?: string): Promise<GitHubMCPClient | null> {
const githubMcpUrl = 'https://api.githubcopilot.com/mcp/'
core.info('Connecting to GitHub MCP server...')
const headers: Record<string, string> = {
Authorization: `Bearer ${token}`,
'X-MCP-Readonly': 'true',
}
// Add toolsets header if specified
if (toolsets && toolsets.trim() !== '') {
headers['X-MCP-Toolsets'] = toolsets
core.info(`Using GitHub MCP toolsets: ${toolsets}`)
} else {
core.info('Using default GitHub MCP toolsets')
}
const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), {
requestInit: {
headers: {
Authorization: `Bearer ${token}`,
'X-MCP-Readonly': 'true',
},
headers,
},
})

View File

@@ -7,9 +7,17 @@ export interface PromptMessage {
content: string
}
export interface ModelParameters {
maxTokens?: number // Deprecated
maxCompletionTokens?: number
temperature?: number
topP?: number
}
export interface PromptConfig {
messages: PromptMessage[]
model?: string
modelParameters?: ModelParameters
responseFormat?: 'text' | 'json_schema'
jsonSchema?: string
}
@@ -101,11 +109,8 @@ export function loadPromptFile(filePath: string, templateVariables: TemplateVari
const fileContent = fs.readFileSync(filePath, 'utf-8')
// Apply template variable substitution
const processedContent = replaceTemplateVariables(fileContent, templateVariables)
try {
const config = yaml.load(processedContent) as PromptConfig
const config = yaml.load(fileContent) as PromptConfig
if (!config.messages || !Array.isArray(config.messages)) {
throw new Error('Prompt file must contain a "messages" array')
@@ -121,6 +126,14 @@ export function loadPromptFile(filePath: string, templateVariables: TemplateVari
}
}
// Prepare messages by replacing template variables with actual content
config.messages = config.messages.map(msg => {
return {
...msg,
content: replaceTemplateVariables(msg.content, templateVariables),
}
})
return config
} catch (error) {
throw new Error(`Failed to parse prompt file: ${error instanceof Error ? error.message : 'Unknown error'}`)