From 06bfbce3a12ea8e3fd572df58144f75f8a62ef91 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:32:55 +0000 Subject: [PATCH 01/12] Add ACTIONS_ORCHESTRATION_ID support to http-client user-agent Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- __tests__/basics.test.ts | 31 +++++++++++++++++++++++++++++++ src/index.ts | 10 +++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/__tests__/basics.test.ts b/__tests__/basics.test.ts index 254a8ec..159cf7b 100644 --- a/__tests__/basics.test.ts +++ b/__tests__/basics.test.ts @@ -374,4 +374,35 @@ describe('basics', () => { httpm.MediaTypes.ApplicationJson ) }) + + it('appends orchestration ID to user-agent when ACTIONS_ORCHESTRATION_ID is set', async () => { + const orchId = 'test-orch-id-12345' + process.env['ACTIONS_ORCHESTRATION_ID'] = orchId + + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests') + const res: httpm.HttpClientResponse = await http.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.headers['user-agent']).toBe( + `http-client-tests (gh_orch_id:${orchId})` + ) + + delete process.env['ACTIONS_ORCHESTRATION_ID'] + }) + + it('does not modify user-agent when ACTIONS_ORCHESTRATION_ID is not set', async () => { + delete process.env['ACTIONS_ORCHESTRATION_ID'] + + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests') + const res: httpm.HttpClientResponse = await http.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.headers['user-agent']).toBe('http-client-tests') + }) }) diff --git a/src/index.ts b/src/index.ts index 5691e89..51a01ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -599,7 +599,7 @@ export class HttpClient { info.options.method = method info.options.headers = this._mergeHeaders(headers) if (this.userAgent != null) { - info.options.headers['user-agent'] = this.userAgent + info.options.headers['user-agent'] = this._getUserAgentWithOrchestrationId(this.userAgent) } info.options.agent = this._getAgent(info.parsedUrl) @@ -816,6 +816,14 @@ export class HttpClient { return proxyAgent } + private _getUserAgentWithOrchestrationId(userAgent: string): string { + const orchId = process.env['ACTIONS_ORCHESTRATION_ID'] + if (orchId) { + return `${userAgent} (gh_orch_id:${orchId})` + } + return userAgent + } + private async _performExponentialBackoff(retryNumber: number): Promise { retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber) const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber) From 4d32a9417468dbab249b065e53d0f3842bead366 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:36:15 +0000 Subject: [PATCH 02/12] Format code and update .gitignore to exclude .nx cache Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- __tests__/basics.test.ts | 6 +++--- src/index.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/__tests__/basics.test.ts b/__tests__/basics.test.ts index 159cf7b..5391fc7 100644 --- a/__tests__/basics.test.ts +++ b/__tests__/basics.test.ts @@ -378,7 +378,7 @@ describe('basics', () => { it('appends orchestration ID to user-agent when ACTIONS_ORCHESTRATION_ID is set', async () => { const orchId = 'test-orch-id-12345' process.env['ACTIONS_ORCHESTRATION_ID'] = orchId - + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests') const res: httpm.HttpClientResponse = await http.get( 'https://postman-echo.com/get' @@ -389,13 +389,13 @@ describe('basics', () => { expect(obj.headers['user-agent']).toBe( `http-client-tests (gh_orch_id:${orchId})` ) - + delete process.env['ACTIONS_ORCHESTRATION_ID'] }) it('does not modify user-agent when ACTIONS_ORCHESTRATION_ID is not set', async () => { delete process.env['ACTIONS_ORCHESTRATION_ID'] - + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests') const res: httpm.HttpClientResponse = await http.get( 'https://postman-echo.com/get' diff --git a/src/index.ts b/src/index.ts index 51a01ac..f2005dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -599,7 +599,8 @@ export class HttpClient { info.options.method = method info.options.headers = this._mergeHeaders(headers) if (this.userAgent != null) { - info.options.headers['user-agent'] = this._getUserAgentWithOrchestrationId(this.userAgent) + info.options.headers['user-agent'] = + this._getUserAgentWithOrchestrationId(this.userAgent) } info.options.agent = this._getAgent(info.parsedUrl) From 9cc24ce294da47add39a9c1fdd5e3ac2c4cd5434 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:26:29 +0000 Subject: [PATCH 03/12] Use product/version format and sanitize orchestration ID Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- __tests__/basics.test.ts | 21 ++++++++++++++++++++- src/index.ts | 5 ++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/__tests__/basics.test.ts b/__tests__/basics.test.ts index 5391fc7..3104306 100644 --- a/__tests__/basics.test.ts +++ b/__tests__/basics.test.ts @@ -387,7 +387,26 @@ describe('basics', () => { const body: string = await res.readBody() const obj = JSON.parse(body) expect(obj.headers['user-agent']).toBe( - `http-client-tests (gh_orch_id:${orchId})` + `http-client-tests github_orchestration_id/${orchId}` + ) + + delete process.env['ACTIONS_ORCHESTRATION_ID'] + }) + + it('sanitizes invalid characters in orchestration ID', async () => { + const orchId = 'test (with) special/chars' + process.env['ACTIONS_ORCHESTRATION_ID'] = orchId + + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests') + const res: httpm.HttpClientResponse = await http.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + // Spaces, parentheses, and slashes should be replaced with underscores + expect(obj.headers['user-agent']).toBe( + 'http-client-tests github_orchestration_id/test__with__special_chars' ) delete process.env['ACTIONS_ORCHESTRATION_ID'] diff --git a/src/index.ts b/src/index.ts index f2005dc..852377c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -820,7 +820,10 @@ export class HttpClient { private _getUserAgentWithOrchestrationId(userAgent: string): string { const orchId = process.env['ACTIONS_ORCHESTRATION_ID'] if (orchId) { - return `${userAgent} (gh_orch_id:${orchId})` + // Sanitize the orchestration ID to ensure it contains only valid token characters + // Valid characters: alphanumeric, !, #, $, %, &, ', *, +, -, ., ^, _, `, |, ~ + const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+\-.^_`|~]/g, '_') + return `${userAgent} github_orchestration_id/${sanitizedId}` } return userAgent } From fbde669eef77356e49afc00109c782bd6fe1e34b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:28:52 +0000 Subject: [PATCH 04/12] Fix regex to properly escape hyphen in character class Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 852377c..1512c2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -822,7 +822,7 @@ export class HttpClient { if (orchId) { // Sanitize the orchestration ID to ensure it contains only valid token characters // Valid characters: alphanumeric, !, #, $, %, &, ', *, +, -, ., ^, _, `, |, ~ - const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+\-.^_`|~]/g, '_') + const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+.\-^_`|~]/g, '_') return `${userAgent} github_orchestration_id/${sanitizedId}` } return userAgent From dede5d42d0d2ff7d466457ac619e00fc1966efdc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:30:09 +0000 Subject: [PATCH 05/12] Move hyphen to end of character class for cleaner regex Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 1512c2c..80d81ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -822,7 +822,7 @@ export class HttpClient { if (orchId) { // Sanitize the orchestration ID to ensure it contains only valid token characters // Valid characters: alphanumeric, !, #, $, %, &, ', *, +, -, ., ^, _, `, |, ~ - const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+.\-^_`|~]/g, '_') + const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+.^_`|~-]/g, '_') return `${userAgent} github_orchestration_id/${sanitizedId}` } return userAgent From 6b8988d7fb918b553a1410a61ef2d8808761f5c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:20:43 +0000 Subject: [PATCH 06/12] Rename github_orchestration_id to actions_orchestration_id Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- __tests__/basics.test.ts | 4 ++-- src/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/basics.test.ts b/__tests__/basics.test.ts index 3104306..d47c120 100644 --- a/__tests__/basics.test.ts +++ b/__tests__/basics.test.ts @@ -387,7 +387,7 @@ describe('basics', () => { const body: string = await res.readBody() const obj = JSON.parse(body) expect(obj.headers['user-agent']).toBe( - `http-client-tests github_orchestration_id/${orchId}` + `http-client-tests actions_orchestration_id/${orchId}` ) delete process.env['ACTIONS_ORCHESTRATION_ID'] @@ -406,7 +406,7 @@ describe('basics', () => { const obj = JSON.parse(body) // Spaces, parentheses, and slashes should be replaced with underscores expect(obj.headers['user-agent']).toBe( - 'http-client-tests github_orchestration_id/test__with__special_chars' + 'http-client-tests actions_orchestration_id/test__with__special_chars' ) delete process.env['ACTIONS_ORCHESTRATION_ID'] diff --git a/src/index.ts b/src/index.ts index 80d81ae..d216277 100644 --- a/src/index.ts +++ b/src/index.ts @@ -823,7 +823,7 @@ export class HttpClient { // Sanitize the orchestration ID to ensure it contains only valid token characters // Valid characters: alphanumeric, !, #, $, %, &, ', *, +, -, ., ^, _, `, |, ~ const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+.^_`|~-]/g, '_') - return `${userAgent} github_orchestration_id/${sanitizedId}` + return `${userAgent} actions_orchestration_id/${sanitizedId}` } return userAgent } From 4777b1b6f67bc2ba6d3fc6d3cc93d6b352d596e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:30:28 +0000 Subject: [PATCH 07/12] Move orchestration ID logic to constructor for efficiency Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- src/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index d216277..d3c2c95 100644 --- a/src/index.ts +++ b/src/index.ts @@ -148,6 +148,8 @@ export class HttpClient { requestOptions?: ifm.RequestOptions ) { this.userAgent = userAgent + ? this._getUserAgentWithOrchestrationId(userAgent) + : undefined this.handlers = handlers || [] this.requestOptions = requestOptions if (requestOptions) { @@ -599,8 +601,7 @@ export class HttpClient { info.options.method = method info.options.headers = this._mergeHeaders(headers) if (this.userAgent != null) { - info.options.headers['user-agent'] = - this._getUserAgentWithOrchestrationId(this.userAgent) + info.options.headers['user-agent'] = this.userAgent } info.options.agent = this._getAgent(info.parsedUrl) From b6fa82dca759c83b5626a2251c463a4189bb4b38 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:37:28 +0000 Subject: [PATCH 08/12] Restrict sanitization to only allow 0-9, a-z, _, -, . Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index d3c2c95..e2e21f5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -821,9 +821,9 @@ export class HttpClient { private _getUserAgentWithOrchestrationId(userAgent: string): string { const orchId = process.env['ACTIONS_ORCHESTRATION_ID'] if (orchId) { - // Sanitize the orchestration ID to ensure it contains only valid token characters - // Valid characters: alphanumeric, !, #, $, %, &, ', *, +, -, ., ^, _, `, |, ~ - const sanitizedId = orchId.replace(/[^a-zA-Z0-9!#$%&'*+.^_`|~-]/g, '_') + // Sanitize the orchestration ID to ensure it contains only valid characters + // Valid characters: 0-9, a-z, _, -, . + const sanitizedId = orchId.replace(/[^a-z0-9_.\-]/gi, '_') return `${userAgent} actions_orchestration_id/${sanitizedId}` } return userAgent From 8b5543ec19bde3733bf058be67f2a2afc067fec8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:03:26 +0000 Subject: [PATCH 09/12] Simplify constructor by handling undefined in helper method Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- src/index.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index e2e21f5..352fa10 100644 --- a/src/index.ts +++ b/src/index.ts @@ -147,9 +147,7 @@ export class HttpClient { handlers?: ifm.RequestHandler[], requestOptions?: ifm.RequestOptions ) { - this.userAgent = userAgent - ? this._getUserAgentWithOrchestrationId(userAgent) - : undefined + this.userAgent = this._getUserAgentWithOrchestrationId(userAgent) this.handlers = handlers || [] this.requestOptions = requestOptions if (requestOptions) { @@ -818,7 +816,12 @@ export class HttpClient { return proxyAgent } - private _getUserAgentWithOrchestrationId(userAgent: string): string { + private _getUserAgentWithOrchestrationId( + userAgent?: string + ): string | undefined { + if (!userAgent) { + return undefined + } const orchId = process.env['ACTIONS_ORCHESTRATION_ID'] if (orchId) { // Sanitize the orchestration ID to ensure it contains only valid characters From ec91b77bd8df48e786de5acc725598b497fc1733 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:10:12 +0000 Subject: [PATCH 10/12] Add default user-agent 'actions/http-client' when none provided Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- __tests__/basics.test.ts | 2 +- src/index.ts | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/__tests__/basics.test.ts b/__tests__/basics.test.ts index d47c120..8e76a38 100644 --- a/__tests__/basics.test.ts +++ b/__tests__/basics.test.ts @@ -60,7 +60,7 @@ describe('basics', () => { const body: string = await res.readBody() const obj = JSON.parse(body) expect(obj.url).toBe('https://postman-echo.com/get') - expect(obj.headers['user-agent']).toBeFalsy() + expect(obj.headers['user-agent']).toBe('actions/http-client') }) /* TODO write a mock rather then relying on a third party diff --git a/src/index.ts b/src/index.ts index 352fa10..4dfddeb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -816,20 +816,16 @@ export class HttpClient { return proxyAgent } - private _getUserAgentWithOrchestrationId( - userAgent?: string - ): string | undefined { - if (!userAgent) { - return undefined - } + private _getUserAgentWithOrchestrationId(userAgent?: string): string { + const baseUserAgent = userAgent || 'actions/http-client' const orchId = process.env['ACTIONS_ORCHESTRATION_ID'] if (orchId) { // Sanitize the orchestration ID to ensure it contains only valid characters // Valid characters: 0-9, a-z, _, -, . const sanitizedId = orchId.replace(/[^a-z0-9_.\-]/gi, '_') - return `${userAgent} actions_orchestration_id/${sanitizedId}` + return `${baseUserAgent} actions_orchestration_id/${sanitizedId}` } - return userAgent + return baseUserAgent } private async _performExponentialBackoff(retryNumber: number): Promise { From 86f6cef4e52357413f848f1e6a145192ea9ad835 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:30:18 +0000 Subject: [PATCH 11/12] Fix lint error: remove unnecessary escape in regex Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 4dfddeb..2aee17d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -822,7 +822,7 @@ export class HttpClient { if (orchId) { // Sanitize the orchestration ID to ensure it contains only valid characters // Valid characters: 0-9, a-z, _, -, . - const sanitizedId = orchId.replace(/[^a-z0-9_.\-]/gi, '_') + const sanitizedId = orchId.replace(/[^a-z0-9_.-]/gi, '_') return `${baseUserAgent} actions_orchestration_id/${sanitizedId}` } return baseUserAgent From 5d2ce8c93201bc29f49efdaeeb81763e24c85271 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Jan 2026 19:19:35 +0000 Subject: [PATCH 12/12] Improve test cleanup and add missing test coverage Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com> --- __tests__/basics.test.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/__tests__/basics.test.ts b/__tests__/basics.test.ts index 8e76a38..2bb57ed 100644 --- a/__tests__/basics.test.ts +++ b/__tests__/basics.test.ts @@ -21,7 +21,10 @@ describe('basics', () => { _http = new httpm.HttpClient('http-client-tests') }) - afterEach(() => {}) + afterEach(() => { + // Clean up environment variable to prevent test pollution + delete process.env['ACTIONS_ORCHESTRATION_ID'] + }) it('constructs', () => { const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests') @@ -389,8 +392,6 @@ describe('basics', () => { expect(obj.headers['user-agent']).toBe( `http-client-tests actions_orchestration_id/${orchId}` ) - - delete process.env['ACTIONS_ORCHESTRATION_ID'] }) it('sanitizes invalid characters in orchestration ID', async () => { @@ -408,8 +409,6 @@ describe('basics', () => { expect(obj.headers['user-agent']).toBe( 'http-client-tests actions_orchestration_id/test__with__special_chars' ) - - delete process.env['ACTIONS_ORCHESTRATION_ID'] }) it('does not modify user-agent when ACTIONS_ORCHESTRATION_ID is not set', async () => { @@ -424,4 +423,20 @@ describe('basics', () => { const obj = JSON.parse(body) expect(obj.headers['user-agent']).toBe('http-client-tests') }) + + it('uses default user-agent with orchestration ID when no custom user-agent provided', async () => { + const orchId = 'test-default-id-12345' + process.env['ACTIONS_ORCHESTRATION_ID'] = orchId + + const http: httpm.HttpClient = new httpm.HttpClient() + const res: httpm.HttpClientResponse = await http.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.headers['user-agent']).toBe( + `actions/http-client actions_orchestration_id/${orchId}` + ) + }) })