@littlebearapps/platform-admin-sdk 2.3.0 → 2.3.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@littlebearapps/platform-admin-sdk",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Platform Admin SDK — scaffold backend infrastructure with workers, circuit breakers, and cost protection for Cloudflare",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1187,10 +1187,10 @@ async function findExistingIssue(
1187
1187
  const alertNumber = alert.metadata?.alert_number as number;
1188
1188
  const ruleId = alert.metadata?.rule_id as string;
1189
1189
 
1190
- const query = `repo:${owner}/${repo} is:issue is:open label:codeql "${ruleId}" in:title "${alertNumber}" in:body`;
1191
-
1190
+ // Use Issues List API (not Search API) more reliable with GitHub App tokens.
1191
+ const labelParam = encodeURIComponent('codeql');
1192
1192
  const response = await fetch(
1193
- `https://api.github.com/search/issues?q=${encodeURIComponent(query)}`,
1193
+ `https://api.github.com/repos/${owner}/${repo}/issues?labels=${labelParam}&state=open&per_page=10&sort=created&direction=desc`,
1194
1194
  {
1195
1195
  headers: {
1196
1196
  Authorization: `Bearer ${env.GITHUB_TOKEN}`,
@@ -1201,16 +1201,15 @@ async function findExistingIssue(
1201
1201
  );
1202
1202
 
1203
1203
  if (!response.ok) {
1204
- log.error('Failed to search for existing issues', { status: response.status });
1204
+ log.error('Failed to list issues by label', { status: response.status });
1205
1205
  return null;
1206
1206
  }
1207
1207
 
1208
- const data = (await response.json()) as {
1209
- items?: Array<{ number: number; title: string; body: string }>;
1210
- };
1208
+ const data = (await response.json()) as
1209
+ Array<{ number: number; title: string; body: string }>;
1211
1210
 
1212
1211
  // Check if any results match this alert number
1213
- const match = data.items?.find((issue) =>
1212
+ const match = data.find((issue) =>
1214
1213
  issue.body?.includes(`Alert Number**: #${alertNumber}`)
1215
1214
  );
1216
1215
 
@@ -200,16 +200,13 @@ export async function processEmailHealthAlerts(
200
200
  try {
201
201
  const issueTitle = `Email Health: ${brandName} ${failure.check_type} failing`;
202
202
 
203
- // Search for an existing OPEN issue before creating a new one.
204
- // This prevents daily duplicate issues when a problem persists across days.
205
- const openIssues = await github.searchIssues(
206
- owner,
207
- repo,
208
- `"${issueTitle}" is:open label:cf:email-health`
209
- );
203
+ // List existing OPEN email health issues before creating a new one.
204
+ // Uses Issues List API (not Search API) more reliable with GitHub App tokens.
205
+ const openIssues = await github.listIssuesByLabel(owner, repo, ['cf:email-health'], 'open');
206
+ const matchingIssue = openIssues.find((issue) => issue.title === issueTitle);
210
207
 
211
- if (openIssues.length > 0) {
212
- const existing = openIssues[0];
208
+ if (matchingIssue) {
209
+ const existing = matchingIssue;
213
210
  await github.addComment(
214
211
  owner,
215
212
  repo,
@@ -237,16 +237,15 @@ export async function processGapAlert(
237
237
  const github = new GitHubClient(env);
238
238
 
239
239
  try {
240
- // Search for an existing OPEN gap alert issue before creating a new one.
241
- // This prevents daily duplicate issues when coverage stays below threshold.
242
- const openIssues = await github.searchIssues(
243
- owner,
244
- repo,
245
- `"Data Coverage Gap: ${event.project}" is:open label:cf:gap-alert`
240
+ // List existing OPEN gap alert issues before creating a new one.
241
+ // Uses Issues List API (not Search API) more reliable with GitHub App tokens.
242
+ const openIssues = await github.listIssuesByLabel(owner, repo, ['cf:gap-alert'], 'open');
243
+ const matchingIssue = openIssues.find((issue) =>
244
+ issue.title.includes(`Data Coverage Gap: ${event.project}`)
246
245
  );
247
246
 
248
- if (openIssues.length > 0) {
249
- const existing = openIssues[0];
247
+ if (matchingIssue) {
248
+ const existing = matchingIssue;
250
249
  await github.addComment(
251
250
  owner,
252
251
  repo,
@@ -277,6 +277,32 @@ export class GitHubClient {
277
277
  return this.request('GET', `/repos/${owner}/${repo}/issues/${issueNumber}`);
278
278
  }
279
279
 
280
+ /**
281
+ * List issues filtered by labels using the Issues List API.
282
+ * More reliable than Search API with GitHub App installation tokens.
283
+ * @see https://docs.github.com/en/rest/issues/issues#list-repository-issues
284
+ */
285
+ async listIssuesByLabel(
286
+ owner: string,
287
+ repo: string,
288
+ labels: string[],
289
+ state: 'open' | 'closed' = 'open'
290
+ ): Promise<
291
+ Array<{
292
+ number: number;
293
+ state: string;
294
+ title: string;
295
+ body: string | null;
296
+ labels: Array<{ name: string }>;
297
+ }>
298
+ > {
299
+ const labelParam = labels.join(',');
300
+ return this.request(
301
+ 'GET',
302
+ `/repos/${owner}/${repo}/issues?labels=${encodeURIComponent(labelParam)}&state=${state}&per_page=10&sort=created&direction=desc`
303
+ );
304
+ }
305
+
280
306
  /**
281
307
  * Search for issues using GitHub's search API.
282
308
  * Retries once on 403 (rate limit) with a 1s delay.