@atolis-hq/wake 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,8 @@
1
1
  import { Octokit } from '@octokit/rest';
2
+ import { createEtagCache, fetchPaginatedWithEtag, fetchSingleWithEtag, } from './github-etag-cache.js';
2
3
  export function createGitHubClient(token) {
3
4
  const octokit = new Octokit({ auth: token });
5
+ const etagCache = createEtagCache();
4
6
  return {
5
7
  async getAuthenticatedLogin() {
6
8
  const { data } = await octokit.rest.users.getAuthenticated();
@@ -12,27 +14,31 @@ export function createGitHubClient(token) {
12
14
  // issues. Stop paginating as soon as the cap is reached.
13
15
  async listIssues(owner, repo, maxResults, since) {
14
16
  const perPage = Math.min(maxResults, 100);
15
- const results = [];
16
- for await (const { data } of octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
17
- owner,
18
- repo,
19
- state: 'all',
20
- per_page: perPage,
21
- ...(since === undefined ? {} : { since }),
22
- })) {
23
- results.push(...data);
24
- if (results.length >= maxResults) {
25
- break;
26
- }
27
- }
28
- return results.slice(0, maxResults);
17
+ return fetchPaginatedWithEtag({
18
+ cache: etagCache,
19
+ cacheKey: `issues:${owner}/${repo}`,
20
+ maxResults,
21
+ pages: (headers) => octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
22
+ owner,
23
+ repo,
24
+ state: 'all',
25
+ per_page: perPage,
26
+ ...(since === undefined ? {} : { since }),
27
+ ...(headers === undefined ? {} : { headers }),
28
+ }),
29
+ });
29
30
  },
30
31
  async listComments(owner, repo, issueNumber, perPage) {
31
- return octokit.paginate(octokit.rest.issues.listComments, {
32
- owner,
33
- repo,
34
- issue_number: issueNumber,
35
- per_page: perPage,
32
+ return fetchPaginatedWithEtag({
33
+ cache: etagCache,
34
+ cacheKey: `issue-comments:${owner}/${repo}#${issueNumber}`,
35
+ pages: (headers) => octokit.paginate.iterator(octokit.rest.issues.listComments, {
36
+ owner,
37
+ repo,
38
+ issue_number: issueNumber,
39
+ per_page: perPage,
40
+ ...(headers === undefined ? {} : { headers }),
41
+ }),
36
42
  });
37
43
  },
38
44
  async createComment(owner, repo, issueNumber, body) {
@@ -60,10 +66,18 @@ export function createGitHubClient(token) {
60
66
  return data;
61
67
  },
62
68
  async getRequiredStatusChecks(owner, repo, branch) {
63
- const { data } = await octokit.rest.repos.getBranch({
64
- owner,
65
- repo,
66
- branch,
69
+ const data = await fetchSingleWithEtag({
70
+ cache: etagCache,
71
+ cacheKey: `required-status-checks:${owner}/${repo}@${branch}`,
72
+ request: async (headers) => {
73
+ const response = await octokit.rest.repos.getBranch({
74
+ owner,
75
+ repo,
76
+ branch,
77
+ ...(headers === undefined ? {} : { headers }),
78
+ });
79
+ return { data: response.data, headers: response.headers ?? {} };
80
+ },
67
81
  });
68
82
  const requiredStatusChecks = data.protection?.required_status_checks;
69
83
  return {
@@ -74,52 +88,77 @@ export function createGitHubClient(token) {
74
88
  };
75
89
  },
76
90
  async listCheckRunsForRef(owner, repo, ref) {
77
- const { data } = await octokit.rest.checks.listForRef({
78
- owner,
79
- repo,
80
- ref,
81
- per_page: 100,
91
+ const data = await fetchSingleWithEtag({
92
+ cache: etagCache,
93
+ cacheKey: `check-runs:${owner}/${repo}@${ref}`,
94
+ request: async (headers) => {
95
+ const response = await octokit.rest.checks.listForRef({
96
+ owner,
97
+ repo,
98
+ ref,
99
+ per_page: 100,
100
+ ...(headers === undefined ? {} : { headers }),
101
+ });
102
+ return { data: response.data, headers: response.headers ?? {} };
103
+ },
82
104
  });
83
105
  return data.check_runs;
84
106
  },
85
107
  async getCombinedStatusForRef(owner, repo, ref) {
86
- const { data } = await octokit.rest.repos.getCombinedStatusForRef({
87
- owner,
88
- repo,
89
- ref,
108
+ const data = await fetchSingleWithEtag({
109
+ cache: etagCache,
110
+ cacheKey: `combined-status:${owner}/${repo}@${ref}`,
111
+ request: async (headers) => {
112
+ const response = await octokit.rest.repos.getCombinedStatusForRef({
113
+ owner,
114
+ repo,
115
+ ref,
116
+ ...(headers === undefined ? {} : { headers }),
117
+ });
118
+ return { data: response.data, headers: response.headers ?? {} };
119
+ },
90
120
  });
91
121
  return data.statuses;
92
122
  },
93
123
  async listPullRequests(owner, repo, maxResults) {
94
124
  const perPage = Math.min(maxResults, 100);
95
- const results = [];
96
- for await (const { data } of octokit.paginate.iterator(octokit.rest.pulls.list, {
97
- owner,
98
- repo,
99
- state: 'open',
100
- per_page: perPage,
101
- })) {
102
- results.push(...data);
103
- if (results.length >= maxResults) {
104
- break;
105
- }
106
- }
107
- return results.slice(0, maxResults);
125
+ return fetchPaginatedWithEtag({
126
+ cache: etagCache,
127
+ cacheKey: `pulls:${owner}/${repo}`,
128
+ maxResults,
129
+ pages: (headers) => octokit.paginate.iterator(octokit.rest.pulls.list, {
130
+ owner,
131
+ repo,
132
+ state: 'open',
133
+ per_page: perPage,
134
+ ...(headers === undefined ? {} : { headers }),
135
+ }),
136
+ });
108
137
  },
109
138
  async listReviews(owner, repo, pullNumber, perPage) {
110
- return octokit.paginate(octokit.rest.pulls.listReviews, {
111
- owner,
112
- repo,
113
- pull_number: pullNumber,
114
- per_page: perPage,
139
+ return fetchPaginatedWithEtag({
140
+ cache: etagCache,
141
+ cacheKey: `pr-reviews:${owner}/${repo}#${pullNumber}`,
142
+ pages: (headers) => octokit.paginate.iterator(octokit.rest.pulls.listReviews, {
143
+ owner,
144
+ repo,
145
+ pull_number: pullNumber,
146
+ per_page: perPage,
147
+ ...(headers === undefined ? {} : { headers }),
148
+ }),
115
149
  });
116
150
  },
117
151
  async listReviewComments(owner, repo, pullNumber, perPage) {
118
- return octokit.paginate(octokit.rest.pulls.listReviewComments, {
119
- owner,
120
- repo,
121
- pull_number: pullNumber,
122
- per_page: perPage,
152
+ return fetchPaginatedWithEtag({
153
+ cache: etagCache,
154
+ cacheKey: `pr-review-comments:${owner}/${repo}#${pullNumber}`,
155
+ pages: (headers) => octokit.paginate.iterator(octokit.rest.pulls.listReviewComments, {
156
+ owner,
157
+ repo,
158
+ pull_number: pullNumber,
159
+ per_page: perPage,
160
+ ...(headers === undefined ? {} : { headers }),
161
+ }),
123
162
  });
124
163
  },
125
164
  async replyToReviewComment(owner, repo, pullNumber, commentId, body) {
@@ -0,0 +1,57 @@
1
+ export function createEtagCache() {
2
+ return new Map();
3
+ }
4
+ function isNotModifiedError(error) {
5
+ return (typeof error === 'object' &&
6
+ error !== null &&
7
+ 'status' in error &&
8
+ error.status === 304);
9
+ }
10
+ export async function fetchSingleWithEtag(input) {
11
+ const cached = input.cache.get(input.cacheKey);
12
+ try {
13
+ const response = await input.request(cached === undefined ? undefined : { 'if-none-match': cached.etag });
14
+ if (typeof response.headers.etag === 'string') {
15
+ input.cache.set(input.cacheKey, { etag: response.headers.etag, data: response.data });
16
+ }
17
+ return response.data;
18
+ }
19
+ catch (error) {
20
+ if (isNotModifiedError(error) && cached !== undefined) {
21
+ return cached.data;
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ export async function fetchPaginatedWithEtag(input) {
27
+ const cached = input.cache.get(input.cacheKey);
28
+ const results = [];
29
+ let pageCount = 0;
30
+ let lastHeaders = {};
31
+ try {
32
+ for await (const page of input.pages(cached === undefined ? undefined : { 'if-none-match': cached.etag })) {
33
+ pageCount += 1;
34
+ lastHeaders = page.headers;
35
+ results.push(...page.data);
36
+ if (input.maxResults !== undefined && results.length >= input.maxResults) {
37
+ break;
38
+ }
39
+ }
40
+ }
41
+ catch (error) {
42
+ if (isNotModifiedError(error) && cached !== undefined) {
43
+ return input.maxResults === undefined ? cached.data : cached.data.slice(0, input.maxResults);
44
+ }
45
+ throw error;
46
+ }
47
+ const truncated = input.maxResults !== undefined && results.length > input.maxResults
48
+ ? results.slice(0, input.maxResults)
49
+ : results;
50
+ if (pageCount <= 1 && typeof lastHeaders.etag === 'string') {
51
+ input.cache.set(input.cacheKey, { etag: lastHeaders.etag, data: truncated });
52
+ }
53
+ else {
54
+ input.cache.delete(input.cacheKey);
55
+ }
56
+ return truncated;
57
+ }
@@ -1 +1 @@
1
- export const wakeVersion = "0.1.16+g97c5859";
1
+ export const wakeVersion = "0.1.17+gdbc29dc";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {