pi-unsloth-webtools 0.1.0 → 0.2.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 (3) hide show
  1. package/README.md +19 -13
  2. package/html-to-md.ts +31 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,6 +4,20 @@ A [pi](https://github.com/earendil-works/pi-coding-agent) extension providing `w
4
4
  `web_fetch` tools, ported from the Unsloth Studio codebase
5
5
  ([`unslothai/unsloth`](https://github.com/unslothai/unsloth), `studio/backend/core/inference/`).
6
6
 
7
+ ## Install
8
+
9
+ ```sh
10
+ pi install npm:pi-unsloth-webtools
11
+ ```
12
+
13
+ or add `npm:pi-unsloth-webtools` to the `packages` array in `~/.pi/agent/settings.json`.
14
+
15
+ To install from source instead:
16
+
17
+ ```sh
18
+ pi install /path/to/pi-unsloth-webtools
19
+ ```
20
+
7
21
  ## What it does
8
22
 
9
23
  ### web_search
@@ -38,9 +52,9 @@ Port of Studio's `_fetch_page_text` / `_fetch_url_raw` pipeline:
38
52
  object streams, all filters, ToUnicode fonts, encryption detection, and a
39
53
  pymupdf4llm-style markdown layer (headings, bold/italic, code fences, links, tables)
40
54
  with Studio's corrupted/incomplete fallback to plain text.
41
- - Content sniffing: MIME allow/deny, binary magic signatures, PDF magic + text extraction
42
- (built-in zlib-based extractor, not pymupdf-grade), charset decoding (declared charset, BOM
43
- sniffing for UTF-8/16/32, cp1252 rescue for mislabeled single-byte pages).
55
+ - Content sniffing: MIME allow/deny, binary magic signatures, PDF magic detection, and charset
56
+ decoding (declared charset, BOM sniffing for UTF-8/16/32, cp1252 rescue for mislabeled
57
+ single-byte pages).
44
58
  - HTML → Markdown conversion ported from Studio's dependency-free `_html_to_md.py`: headings,
45
59
  links, emphasis, lists, tables, blockquotes, code fences, entity decoding; hidden-element
46
60
  stripping (`hidden`, `aria-hidden`, inline styles); `<article>`/`<main>` main-content scoping
@@ -51,16 +65,8 @@ Port of Studio's `_fetch_page_text` / `_fetch_url_raw` pipeline:
51
65
  - HTML entity decoding replicates CPython's `html.unescape` (full 2,231-entry HTML5 table,
52
66
  longest-prefix rule, Windows-1252 numeric mappings), matching Studio byte-for-byte.
53
67
 
54
- See `ROADMAP.md` for the remaining gaps (PDF extraction, TLS fingerprinting, proxies).
55
-
56
- ## Install
57
-
58
- ```sh
59
- pi install /path/to/pi-unsloth-webtools
60
- ```
61
-
62
- or add `npm:pi-unsloth-webtools` (once published) to the `packages` array in
63
- `~/.pi/agent/settings.json`.
68
+ See `ROADMAP.md` for the remaining gaps (per-line styling, table detection, TLS
69
+ fingerprinting, proxies).
64
70
 
65
71
  ## Development
66
72
 
package/html-to-md.ts CHANGED
@@ -227,6 +227,21 @@ interface HtmlHandlers {
227
227
  const START_TAG_NAME_RE = /^[a-zA-Z][^\s/>]*/;
228
228
  const ATTR_NAME_RE = /^[^\s=/>]+/;
229
229
 
230
+ const RAW_TEXT_TAGS = [
231
+ "script",
232
+ "style",
233
+ "textarea",
234
+ "title",
235
+ "xmp",
236
+ "iframe",
237
+ "noembed",
238
+ "noframes",
239
+ ];
240
+
241
+ const RAW_TEXT_CLOSERS: Record<string, RegExp> = Object.fromEntries(
242
+ RAW_TEXT_TAGS.map((name) => [name, new RegExp(`</${name}\\s*>`, "i")]),
243
+ );
244
+
230
245
  function parseAttrsUntilClose(input: string, pos: number): [AttrDict, number, boolean] {
231
246
  const attrs: AttrDict = {};
232
247
  while (pos < input.length) {
@@ -346,8 +361,22 @@ export function feedHtml(input: string, handlers: HtmlHandlers): void {
346
361
  continue;
347
362
  }
348
363
  emitText(input.slice(textStart, i));
349
- if (tag.kind === "start") handlers.handleStartTag(tag.name!, tag.attrs!);
350
- else if (tag.kind === "startend") handlers.handleStartEndTag(tag.name!, tag.attrs!);
364
+ if (tag.kind === "start") {
365
+ const rawCloser = RAW_TEXT_CLOSERS[tag.name!];
366
+ if (rawCloser) {
367
+ const after = input.slice(tag.end);
368
+ const closeMatch = rawCloser.exec(after);
369
+ if (closeMatch) {
370
+ handlers.handleStartTag(tag.name!, tag.attrs!);
371
+ emitText(after.slice(0, closeMatch.index));
372
+ handlers.handleEndTag(tag.name!);
373
+ textStart = tag.end + closeMatch.index + closeMatch[0].length;
374
+ i = textStart;
375
+ continue;
376
+ }
377
+ }
378
+ handlers.handleStartTag(tag.name!, tag.attrs!);
379
+ } else if (tag.kind === "startend") handlers.handleStartEndTag(tag.name!, tag.attrs!);
351
380
  else if (tag.kind === "end") handlers.handleEndTag(tag.name!);
352
381
  textStart = tag.end;
353
382
  i = tag.end;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-unsloth-webtools",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Pi extension: web_search and web_fetch tools ported from the Unsloth Studio codebase (DuckDuckGo search, SSRF-safe direct fetching, HTML-to-Markdown extraction)",
6
6
  "main": "index.ts",