From f1d59cc19fd1dead5a049f31b4ca009dfe9281b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Apr 2026 16:31:37 +0000 Subject: [PATCH 1/2] Initial plan From e10117dba01b07035e4e53f02f317c9bdbbf63a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Apr 2026 16:52:04 +0000 Subject: [PATCH 2/2] fix: preserve .endpoint on proxied Octokit methods to fix route.endpoint is not a function error Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b14847d2-3cd9-4f14-af53-ca1dfc233683 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/github_rate_limit_logger.cjs | 14 +++++- .../js/github_rate_limit_logger.test.cjs | 45 +++++++++++++++++++ actions/setup/setup.sh | 2 + 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/actions/setup/js/github_rate_limit_logger.cjs b/actions/setup/js/github_rate_limit_logger.cjs index 1ddd7d717c1..4959c4a4c17 100644 --- a/actions/setup/js/github_rate_limit_logger.cjs +++ b/actions/setup/js/github_rate_limit_logger.cjs @@ -183,11 +183,23 @@ function createRateLimitAwareGithub(github) { get(target, method) { const fn = target[method]; if (typeof fn !== "function") return fn; - return async (/** @type {any[]} */ ...args) => { + const wrapper = async (/** @type {any[]} */ ...args) => { const response = await fn.apply(target, args); logRateLimitFromResponse(response, `${namespaceName}.${String(method)}`); return response; }; + // Wrap the wrapper in a Proxy so that Octokit-specific property accesses + // (e.g. .endpoint, used by github.paginate()) fall back to the original fn. + // Without this, github.paginate(github.rest.checks.listForRef, ...) throws + // "route.endpoint is not a function" because the wrapper does not have + // the .endpoint decorator that Octokit endpoint methods carry. + return new Proxy(wrapper, { + get(wrapperTarget, prop) { + const own = Reflect.get(wrapperTarget, prop); + if (own !== undefined) return own; + return Reflect.get(fn, prop); + }, + }); }, }); } diff --git a/actions/setup/js/github_rate_limit_logger.test.cjs b/actions/setup/js/github_rate_limit_logger.test.cjs index 2f695c7124a..eea782cc148 100644 --- a/actions/setup/js/github_rate_limit_logger.test.cjs +++ b/actions/setup/js/github_rate_limit_logger.test.cjs @@ -354,4 +354,49 @@ describe("createRateLimitAwareGithub", () => { expect(first.remaining).toBe(4999); expect(second.remaining).toBe(4998); }); + + it("preserves .endpoint on the wrapped method for github.paginate() compatibility", () => { + // Octokit endpoint-decorated methods (e.g. github.rest.checks.listForRef) carry + // a .endpoint property used by github.paginate() internally. The proxy must + // forward .endpoint to the original function so paginate() doesn't throw + // "route.endpoint is not a function". + const endpointObj = { merge: vi.fn(), defaults: vi.fn() }; + const mockListForRef = vi.fn().mockResolvedValue({ data: [], headers: {} }); + mockListForRef.endpoint = endpointObj; + + const mockGithub = { + rest: { + checks: { listForRef: mockListForRef }, + }, + }; + + const gh = createRateLimitAwareGithub(mockGithub); + const wrapped = gh.rest.checks.listForRef; + + // The wrapped function must expose .endpoint from the original + expect(typeof wrapped).toBe("function"); + expect(wrapped.endpoint).toBe(endpointObj); + expect(wrapped.endpoint.merge).toBe(endpointObj.merge); + }); + + it("allows github.paginate() to call endpoint.merge on the wrapped method", () => { + // Simulate how @octokit/plugin-paginate-rest v9+ uses the endpoint method: + // const options = route.endpoint.merge(parameters) + const mergedOptions = { url: "/repos/o/r/commits/main/check-runs", per_page: 100 }; + const endpointObj = { merge: vi.fn().mockReturnValue(mergedOptions) }; + const mockListForRef = vi.fn().mockResolvedValue({ data: [], headers: {} }); + mockListForRef.endpoint = endpointObj; + + const mockGithub = { + rest: { checks: { listForRef: mockListForRef } }, + }; + + const gh = createRateLimitAwareGithub(mockGithub); + const wrapped = gh.rest.checks.listForRef; + + // paginate() would call this internally + const result = wrapped.endpoint.merge({ owner: "o", repo: "r", ref: "main", per_page: 100 }); + expect(result).toBe(mergedOptions); + expect(endpointObj.merge).toHaveBeenCalledWith({ owner: "o", repo: "r", ref: "main", per_page: 100 }); + }); }); diff --git a/actions/setup/setup.sh b/actions/setup/setup.sh index 50c0c80f3f0..4f3f823f19c 100755 --- a/actions/setup/setup.sh +++ b/actions/setup/setup.sh @@ -216,6 +216,7 @@ MCP_SCRIPTS_FILES=( "read_buffer.cjs" "generate_mcp_scripts_config.cjs" "setup_globals.cjs" + "github_rate_limit_logger.cjs" "error_helpers.cjs" "error_codes.cjs" "constants.cjs" @@ -288,6 +289,7 @@ SAFE_OUTPUTS_FILES=( "write_large_content_to_file.cjs" "generate_compact_schema.cjs" "setup_globals.cjs" + "github_rate_limit_logger.cjs" "error_helpers.cjs" "error_codes.cjs" "constants.cjs"