buddy-workbench 0.2.3 → 0.2.4

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": "buddy-workbench",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -328,6 +328,31 @@ async function searchJiraWithFallbacks(jiraHost, headers, jqlCandidates, fields
328
328
  return [];
329
329
  }
330
330
 
331
+ async function searchJiraWithFallbacksPaginated(jiraHost, headers, jqlCandidates, fields, maxResults = 100) {
332
+ for (const jql of jqlCandidates) {
333
+ if (!jql) continue;
334
+ try {
335
+ const issues = [];
336
+ let startAt = 0;
337
+ let total = 0;
338
+ do {
339
+ const searchUrl = `https://${jiraHost}/rest/api/2/search?jql=${encodeURIComponent(jql)}&startAt=${startAt}&maxResults=${maxResults}&fields=${encodeURIComponent(fields)}`;
340
+ const response = await httpClient.get(searchUrl, { headers });
341
+ if (response.status !== 200 || !Array.isArray(response.data?.issues)) throw new Error(`Jira search returned ${response.status}`);
342
+ const page = response.data.issues;
343
+ issues.push(...page);
344
+ total = Number(response.data.total) || issues.length;
345
+ startAt += page.length;
346
+ if (page.length === 0) break;
347
+ } while (startAt < total);
348
+ return issues;
349
+ } catch {
350
+ // Try the next JQL form when this Jira instance does not support the current one.
351
+ }
352
+ }
353
+ return [];
354
+ }
355
+
331
356
  async function fetchCommentsForIssue(jiraHost, headers, issueKey, maxResults = 50) {
332
357
  try {
333
358
  const url = `https://${jiraHost}/rest/api/2/issue/${encodeURIComponent(issueKey)}/comment?maxResults=${maxResults}`;
@@ -365,6 +390,9 @@ router.get('/recently-commented', async (_req, res) => {
365
390
  const identity = await getJiraIdentity(jiraHost, token);
366
391
 
367
392
  const jqlCandidates = [];
393
+ // This function targets issues where the current user authored a comment. The
394
+ // text search fallbacks below can miss comments that do not mention the author.
395
+ jqlCandidates.push('commentedBy(currentUser()) ORDER BY updated DESC');
368
396
  if (identity.name) {
369
397
  jqlCandidates.push(`comment ~ "\\"${identity.name}\\"" ORDER BY updated DESC`);
370
398
  jqlCandidates.push(`comment ~ "${identity.name}" ORDER BY updated DESC`);
@@ -378,10 +406,10 @@ router.get('/recently-commented', async (_req, res) => {
378
406
  jqlCandidates.push('(assignee = currentUser() OR reporter = currentUser() OR watcher = currentUser()) AND updated >= -60d ORDER BY updated DESC');
379
407
  jqlCandidates.push('updated >= -60d ORDER BY updated DESC');
380
408
 
381
- const issues = await searchJiraWithFallbacks(jiraHost, headers, jqlCandidates, 'summary,priority,status,updated,assignee,reporter', 40);
409
+ const issues = await searchJiraWithFallbacksPaginated(jiraHost, headers, jqlCandidates, 'summary,priority,status,updated,assignee,reporter');
382
410
  const results = [];
383
411
 
384
- const issueCommentsPromises = issues.slice(0, 30).map(async (issue) => {
412
+ const issueCommentsPromises = issues.map(async (issue) => {
385
413
  const key = issue.key;
386
414
  const fields = issue.fields || {};
387
415
  const comments = await fetchCommentsForIssue(jiraHost, headers, key, 50);
@@ -23,7 +23,10 @@ let suppressImageCaptureUntil = 0;
23
23
  let swiftClipboardRetryAt = 0;
24
24
  let mozjpegWasmModule = null;
25
25
  let macClipboardHelper = null;
26
+ let macClipboardHelperStarting = false;
26
27
  let macClipboardHelperRestartTimer = null;
28
+ let macClipboardHelperRestartDelay = 5000;
29
+ let macClipboardHelperBinary = null;
27
30
  let macClipboardHelperWatchdog = null;
28
31
  let macClipboardHelperExitHookRegistered = false;
29
32
  const clipboardErrorCounts = new Map();
@@ -229,6 +232,7 @@ function handleMacClipboardHelperMessage(state, line) {
229
232
 
230
233
  if (message.kind === 'ready') {
231
234
  state.ready = true;
235
+ macClipboardHelperRestartDelay = 5000;
232
236
  markClipboardSuccess('native image helper startup', 'native image helper process');
233
237
  return;
234
238
  }
@@ -300,7 +304,8 @@ function scheduleMacClipboardHelperRestart() {
300
304
  macClipboardHelperRestartTimer = setTimeout(() => {
301
305
  macClipboardHelperRestartTimer = null;
302
306
  startMacClipboardImageHelper();
303
- }, 5000);
307
+ }, macClipboardHelperRestartDelay);
308
+ macClipboardHelperRestartDelay = Math.min(macClipboardHelperRestartDelay * 2, 5 * 60 * 1000);
304
309
  macClipboardHelperRestartTimer.unref();
305
310
  }
306
311
 
@@ -315,7 +320,7 @@ function stopMacClipboardImageHelper() {
315
320
  }
316
321
 
317
322
  function startMacClipboardImageHelper() {
318
- if (process.platform !== 'darwin' || macClipboardHelper || macClipboardHelperRestartTimer) return;
323
+ if (process.platform !== 'darwin' || macClipboardHelper || macClipboardHelperStarting || macClipboardHelperRestartTimer) return;
319
324
  const outputDirectory = join(paths.clipboardDir, 'content');
320
325
  mkdirSync(outputDirectory, { recursive: true, mode: 0o700 });
321
326
  try { chmodSync(outputDirectory, 0o700); } catch (error) {
@@ -323,11 +328,35 @@ function startMacClipboardImageHelper() {
323
328
  return;
324
329
  }
325
330
  const scriptPath = fileURLToPath(new URL('./mac-clipboard-helper.swift', import.meta.url));
331
+ const binaryPath = macClipboardHelperBinary || join(outputDirectory, 'mac-clipboard-helper');
332
+ macClipboardHelperStarting = true;
333
+ void (async () => {
334
+ try {
335
+ if (!macClipboardHelperBinary) {
336
+ await execFileAsync('swiftc', ['-O', scriptPath, '-o', binaryPath], {
337
+ timeout: 120 * 1000,
338
+ maxBuffer: 10 * 1024 * 1024
339
+ });
340
+ chmodSync(binaryPath, 0o700);
341
+ macClipboardHelperBinary = binaryPath;
342
+ }
343
+ launchMacClipboardImageHelper(binaryPath, outputDirectory);
344
+ } catch (error) {
345
+ logClipboardError('native image helper compile', error);
346
+ scheduleMacClipboardHelperRestart();
347
+ } finally {
348
+ macClipboardHelperStarting = false;
349
+ }
350
+ })();
351
+ }
352
+
353
+ function launchMacClipboardImageHelper(binaryPath, outputDirectory) {
326
354
  let child;
327
355
  try {
328
- child = spawn('swift', [scriptPath, outputDirectory], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
356
+ child = spawn(binaryPath, [outputDirectory], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
329
357
  } catch (error) {
330
358
  logClipboardError('native image helper startup', error);
359
+ macClipboardHelperBinary = null;
331
360
  scheduleMacClipboardHelperRestart();
332
361
  return;
333
362
  }
@@ -363,6 +392,7 @@ function startMacClipboardImageHelper() {
363
392
  const details = state.stderr.trim();
364
393
  logClipboardError('native image helper process', new Error(details || `Helper exited with code ${code ?? signal ?? 'unknown'}.`));
365
394
  }
395
+ if (!state.ready) macClipboardHelperBinary = null;
366
396
  scheduleMacClipboardHelperRestart();
367
397
  });
368
398