pi-lean-search 0.6.0 → 0.7.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.
Files changed (2) hide show
  1. package/index.ts +31 -25
  2. package/package.json +2 -2
package/index.ts CHANGED
@@ -4,11 +4,15 @@
4
4
  * Registers the `web-search` tool and a `/searxng-status` diagnostic command.
5
5
  * Manages the `search` status bar slot with health-colored glyphs.
6
6
  *
7
- * Owns the `pi-lean-dimension.search` toolset (co-activated off `pi-lean-dimension.web`):
7
+ * Owns the `pi-lean-dimension.search` toolset:
8
8
  * ● searxng (accent/blue) — healthy and reachable
9
9
  * ● searxng (warning/yellow) — server up but pipeline degraded
10
10
  * ● searxng (error/red) — unreachable
11
11
  * ○ searxng — search tools off
12
+ *
13
+ * Co-activation with the web workflow is portal-owned: `/web on|off|learn`
14
+ * batches the search toolset id directly. Search is independently togglable
15
+ * via /tbox and focus.
12
16
  */
13
17
 
14
18
  import type {
@@ -19,9 +23,11 @@ import type {
19
23
  import {
20
24
  defineToolset,
21
25
  TOOLSET_EVENTS,
22
- getDefaultResolutionMode,
23
26
  } from "pi-tool-masking";
24
- import type { ToolsetSpec, ToolsetChangedEvent } from "pi-tool-masking";
27
+ import type {
28
+ ToolsetSpec,
29
+ ToolsetChangedEvent,
30
+ } from "pi-tool-masking";
25
31
  import { readSearxngUrl } from "./search-config.js";
26
32
  import { webSearchTool, normalizeBaseUrl } from "./web-search-tool.js";
27
33
 
@@ -51,6 +57,12 @@ let _lastDegraded = false;
51
57
  /** Cached ExtensionContext for event-driven glyph rendering. */
52
58
  let _lastCtx: ExtensionContext | null = null;
53
59
 
60
+ /** Unsubscribers for the factory-time glyph-sync listeners — pi re-invokes
61
+ * the cached extension factory per load pass, so these are re-registered
62
+ * each time and torn down in the factory prologue / session_shutdown to avoid
63
+ * stacking duplicate listeners on the shared event bus. */
64
+ let _offSync: (() => void)[] = [];
65
+
54
66
  // ─── Health probes ───────────────────────────────────────────────
55
67
 
56
68
  /**
@@ -177,33 +189,23 @@ export { _resetStateForTest };
177
189
  // ─── Extension entry point ───────────────────────────────────────
178
190
 
179
191
  export default function (pi: ExtensionAPI) {
192
+ // --- Ensure idempotent re-invocation ----------------------------
193
+ // pi reuses the cached extension factory on /resume (same cwd),
194
+ // which re-invokes this function with the same module-level
195
+ // singletons.
196
+ for (const off of _offSync) off();
197
+ _offSync = [];
198
+
180
199
  // ── Register the web-search tool ─────────────────────────
181
200
  pi.registerTool(webSearchTool);
182
201
 
183
202
  // ── Define the pi-lean-dimension.search toolset ───────────
184
203
  // Registers restore handler on session_start / session_tree.
185
- const searchToolset = defineToolset(pi, SEARCH_WEB_SPEC);
186
-
187
- // ── Co-activation: mirror pi-lean-dimension.web changed events ─
188
- // Listen on changed ONLY, not restored.
189
- //
190
- // Focus-mode guard: while allowlist focus holds the line, skip
191
- // co-activation. The focus set is authoritative, so a web `changed` event
192
- // — including one a stale library `doRestore` emits during resume — must
193
- // not disable search or write a focus-indistinguishable {enabled} entry.
194
- pi.events.on(TOOLSET_EVENTS.changed, (data: unknown) => {
195
- const event = data as ToolsetChangedEvent;
196
- if (event.id === "pi-lean-dimension.web") {
197
- if (getDefaultResolutionMode() === "allowlist") return;
198
- if (event.enabled) {
199
- searchToolset.enable(pi);
200
- } else {
201
- searchToolset.disable(pi);
202
- }
203
- }
204
- });
204
+ defineToolset(pi, SEARCH_WEB_SPEC);
205
205
 
206
206
  // ── Keep cached state in sync with library events ────────
207
+ // Glyph cache only — display re-render, not actuation. Co-activation
208
+ // lives in portal's /web command (it batches this toolset id directly).
207
209
  const syncSearchState = (data: unknown) => {
208
210
  const event = data as ToolsetChangedEvent;
209
211
  if (event.id === "pi-lean-dimension.search") {
@@ -211,8 +213,10 @@ export default function (pi: ExtensionAPI) {
211
213
  if (_lastCtx) renderSearchGlyph(_lastCtx);
212
214
  }
213
215
  };
214
- pi.events.on(TOOLSET_EVENTS.changed, syncSearchState);
215
- pi.events.on(TOOLSET_EVENTS.restored, syncSearchState);
216
+ _offSync = [
217
+ pi.events.on(TOOLSET_EVENTS.changed, syncSearchState),
218
+ pi.events.on(TOOLSET_EVENTS.restored, syncSearchState),
219
+ ];
216
220
 
217
221
  // ── Session start: health probe + glyph ──────────────────
218
222
  pi.on("session_start", async (event, ctx) => {
@@ -271,6 +275,8 @@ export default function (pi: ExtensionAPI) {
271
275
 
272
276
  // ── Session shutdown: clean up ───────────────────────────
273
277
  pi.on("session_shutdown", async (_event, ctx) => {
278
+ for (const off of _offSync) off();
279
+ _offSync = [];
274
280
  _lastCtx = null;
275
281
  try {
276
282
  ctx?.ui?.setStatus?.("search", "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lean-search",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
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",
@@ -36,7 +36,7 @@
36
36
  "test": "vitest run"
37
37
  },
38
38
  "dependencies": {
39
- "pi-tool-masking": "^1.3.0"
39
+ "pi-tool-masking": "^2.0.1"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@earendil-works/pi-ai": "*",