pi-lean-search 0.5.0 → 0.6.0

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/index.ts CHANGED
@@ -177,9 +177,6 @@ export { _resetStateForTest };
177
177
  // ─── Extension entry point ───────────────────────────────────────
178
178
 
179
179
  export default function (pi: ExtensionAPI) {
180
- // Read config at startup
181
- _searxngUrl = readSearxngUrl();
182
-
183
180
  // ── Register the web-search tool ─────────────────────────
184
181
  pi.registerTool(webSearchTool);
185
182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lean-search",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "SearXNG web search for Pi. Self-hosted or public instance, no API keys or quotas; returns a setup message instead of erroring when unreachable. Pairs with pi-lean-portal's /web toggle or works standalone.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -393,6 +393,19 @@ export const webSearchTool = defineTool({
393
393
  controller.abort();
394
394
  }, timeoutSeconds * 1000);
395
395
 
396
+ // Abort can surface in either fetch or the body read; label it correctly.
397
+ const abortFail = () => {
398
+ if (timedOut) {
399
+ return fail(
400
+ `Web search timed out after ${timeoutSeconds}s. ` +
401
+ `The SearXNG instance at \`${searxngUrl}\` may be slow ` +
402
+ "or unresponsive.",
403
+ { timedOut: true, timeout: timeoutSeconds },
404
+ );
405
+ }
406
+ return fail("Web search was cancelled.", { cancelled: true });
407
+ };
408
+
396
409
  try {
397
410
  // ── Layer 1: Connection-level error handling ──
398
411
  let response: Response;
@@ -402,20 +415,11 @@ export const webSearchTool = defineTool({
402
415
  headers: { Accept: "application/json" },
403
416
  });
404
417
  } catch (connectionErr) {
405
- clearTimeout(timeoutId);
406
418
  if (
407
419
  connectionErr instanceof DOMException &&
408
420
  connectionErr.name === "AbortError"
409
421
  ) {
410
- if (timedOut) {
411
- return fail(
412
- `Web search timed out after ${timeoutSeconds}s. ` +
413
- `The SearXNG instance at \`${searxngUrl}\` may be slow ` +
414
- "or unresponsive.",
415
- { timedOut: true, timeout: timeoutSeconds },
416
- );
417
- }
418
- return fail("Web search was cancelled.", { cancelled: true });
422
+ return abortFail();
419
423
  }
420
424
  return fail(
421
425
  "Web search connection failed: " +
@@ -426,8 +430,6 @@ export const webSearchTool = defineTool({
426
430
  );
427
431
  }
428
432
 
429
- clearTimeout(timeoutId);
430
-
431
433
  // ── Layer 2: HTTP error handling ──
432
434
  if (!response.ok) {
433
435
  return fail(
@@ -444,6 +446,10 @@ export const webSearchTool = defineTool({
444
446
  ? (JSON.parse(text) as SearXNGResponse)
445
447
  : { results: [], answers: [], suggestions: [] };
446
448
  } catch (parseErr) {
449
+ // Abort can surface here if the body read stalls past the timeout.
450
+ if (parseErr instanceof DOMException && parseErr.name === "AbortError") {
451
+ return abortFail();
452
+ }
447
453
  return fail(
448
454
  "Web search returned unexpected response format. " +
449
455
  "SearXNG may be misconfigured. Error: " +
@@ -545,7 +551,6 @@ export const webSearchTool = defineTool({
545
551
  },
546
552
  };
547
553
  } catch (unexpectedErr) {
548
- clearTimeout(timeoutId);
549
554
  return fail(
550
555
  "An unexpected error occurred during web search: " +
551
556
  (unexpectedErr instanceof Error
@@ -553,6 +558,10 @@ export const webSearchTool = defineTool({
553
558
  : String(unexpectedErr)),
554
559
  { unexpectedError: true },
555
560
  );
561
+ } finally {
562
+ // Timer stays armed through the body read, so `timeout` bounds the
563
+ // whole request, not just the response headers.
564
+ clearTimeout(timeoutId);
556
565
  }
557
566
  },
558
567