summaryrefslogtreecommitdiff
path: root/src/target-store.ts
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-08-03 21:18:02 +0100
committerIgor Soarez <igor@soarez.org>2026-08-03 21:18:02 +0100
commit495de0d5283dd3e4a6ef715b596c4a2892e95915 (patch)
treeaae2ec78d59cb96d8e90c56ab7626a6886d30be2 /src/target-store.ts
Web search for pi through an existing Chrome over CDP
Attaches to a browser that is already running — never launches one — using the same endpoint configuration as pi-browser-harness, so a single /browser-target choice governs both packages. One tool, castle_cdp_search, deliberately not named web_search so it coexists with pi-web-access rather than shadowing it. Notes from validating against castle's Chrome: - tbs=qdr:*, the parameter Google's own Tools menu writes, renders an empty page on this profile; the older as_qdr=* works. Any date filter combined with udm=14 is also empty, so recency drops udm. - Target.createTarget must not be raced against the abort signal: raceAbort abandons the promise but cannot cancel the command, and the command's side effect is a tab nothing is left holding. - A search cancelled while queued has to be removed from the semaphore queue, or the slot handed to it later is never counted back. - A decaying rate-limit block stops serving /sorry/ and returns an empty results page instead, indistinguishable from a genuine zero-hit search.
Diffstat (limited to 'src/target-store.ts')
-rw-r--r--src/target-store.ts42
1 files changed, 42 insertions, 0 deletions
diff --git a/src/target-store.ts b/src/target-store.ts
new file mode 100644
index 0000000..6ff0db4
--- /dev/null
+++ b/src/target-store.ts
@@ -0,0 +1,42 @@
+/**
+ * Read-only view of the target pi-browser-harness persists for `/browser-target`.
+ *
+ * The stored value is the raw string a user would have put in BU_CDP_HTTP
+ * ("local", "10.88.0.25:9223", …) — kept in that vocabulary deliberately, so
+ * there is one resolution path in endpoint.ts and a stored value can never mean
+ * something the variable could not.
+ *
+ * Every read failure degrades to "nothing stored" rather than throwing: a
+ * corrupt or absent file must fall back to the built-in default, not break search.
+ */
+
+import { readFile } from "node:fs/promises";
+import { targetFilePath } from "./paths.ts";
+
+const CURRENT_VERSION = 1;
+
+/**
+ * The persisted target, or null when nothing is stored, the file is missing, or
+ * it cannot be parsed. A file written by a newer version is treated as "nothing
+ * stored" rather than guessed at.
+ */
+export const readStoredTarget = async (): Promise<string | null> => {
+ let raw: string;
+ try {
+ raw = await readFile(targetFilePath(), "utf8");
+ } catch {
+ return null;
+ }
+ try {
+ const parsed: unknown = JSON.parse(raw);
+ if (typeof parsed !== "object" || parsed === null) return null;
+ const file = parsed as Record<string, unknown>;
+ if (file["version"] !== CURRENT_VERSION) return null;
+ const target = file["target"];
+ if (typeof target !== "string") return null;
+ const trimmed = target.trim();
+ return trimmed.length > 0 ? trimmed : null;
+ } catch {
+ return null;
+ }
+};