@pi-unipi/web-api 2.4.0 → 2.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/web-api",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "Web search, read, and summarize tools with provider-based backend selection for Pi coding agent",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -31,7 +31,7 @@
31
31
  "README.md"
32
32
  ],
33
33
  "dependencies": {
34
- "@pi-unipi/core": "2.4.0",
34
+ "@pi-unipi/core": "2.4.1",
35
35
  "defuddle": "^0.18.1",
36
36
  "linkedom": "^0.18.12",
37
37
  "lodash": "^4.17.21",
@@ -35,7 +35,6 @@ import { truncateContent, formatContent } from "./format.js";
35
35
  const MAX_REDIRECTS = 5;
36
36
 
37
37
  /** Maximum alternate link fallbacks to try */
38
- const MAX_ALTERNATE_LINKS = 3;
39
38
 
40
39
  /**
41
40
  * Validate a URL for fetching.
@@ -193,31 +192,6 @@ function findMetaRefresh(document: Document): string | null {
193
192
  return match[1];
194
193
  }
195
194
 
196
- /**
197
- * Check for alternate JSON content links.
198
- *
199
- * @param document - DOM document
200
- * @returns Array of alternate URLs
201
- */
202
- function findAlternateLinks(document: Document): string[] {
203
- const alternates: string[] = [];
204
-
205
- // Look for JSON feeds, oEmbed, etc.
206
- const links = document.querySelectorAll(
207
- 'link[rel="alternate"][type="application/json"], ' +
208
- 'link[rel="alternate"][type="application/ld+json"]'
209
- );
210
-
211
- for (const link of Array.from(links)) {
212
- const href = link.getAttribute("href");
213
- if (href) {
214
- alternates.push(href);
215
- }
216
- }
217
-
218
- return alternates.slice(0, MAX_ALTERNATE_LINKS);
219
- }
220
-
221
195
  /**
222
196
  * Detect content type from response.
223
197
  */
package/src/tools.ts CHANGED
@@ -62,19 +62,6 @@ function getAvailableProviders(capability: WebCapability): WebProvider[] {
62
62
  });
63
63
  }
64
64
 
65
- /**
66
- * Select provider for a capability.
67
- * If sourceRank is specified, use that rank.
68
- * Otherwise, use the lowest-ranked available provider.
69
- */
70
- function selectProvider(
71
- capability: WebCapability,
72
- sourceRank?: number
73
- ): WebProvider {
74
- const candidates = selectProviderChain(capability, sourceRank);
75
- return candidates[0];
76
- }
77
-
78
65
  /**
79
66
  * Build the ordered list of providers to try for a capability.
80
67
  *
@@ -1,168 +0,0 @@
1
- /**
2
- * @unipi/web-api — TUI Progress Renderer
3
- *
4
- * Renders batch fetch progress for TUI display.
5
- */
6
-
7
- import type { FetchProgress, FetchProgressStatus } from "../engine/types.js";
8
-
9
- /** Spinner frames for animation */
10
- const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
11
-
12
- /** Status glyphs */
13
- const STATUS_GLYPHS: Record<FetchProgressStatus, string> = {
14
- queued: "○",
15
- connecting: SPINNER_FRAMES[0],
16
- waiting: SPINNER_FRAMES[0],
17
- loading: SPINNER_FRAMES[0],
18
- processing: SPINNER_FRAMES[0],
19
- done: "✓",
20
- error: "✗",
21
- };
22
-
23
- /**
24
- * Get a spinner frame for the given index.
25
- * Cycles through spinner frames for animation.
26
- *
27
- * @param index - Animation frame index
28
- * @returns Spinner character
29
- */
30
- export function getSpinnerFrame(index: number): string {
31
- return SPINNER_FRAMES[index % SPINNER_FRAMES.length];
32
- }
33
-
34
- /**
35
- * Render a progress bar.
36
- *
37
- * @param percent - Progress percentage (0-100)
38
- * @param width - Bar width in characters
39
- * @returns Progress bar string
40
- */
41
- export function renderProgressBar(percent: number, width: number = 10): string {
42
- const filled = Math.round((percent / 100) * width);
43
- const empty = width - filled;
44
- return "█".repeat(filled) + "░".repeat(empty);
45
- }
46
-
47
- /**
48
- * Truncate a URL for display.
49
- *
50
- * @param url - URL to truncate
51
- * @param maxLength - Maximum length
52
- * @returns Truncated URL
53
- */
54
- function truncateUrl(url: string, maxLength: number): string {
55
- if (url.length <= maxLength) {
56
- return url;
57
- }
58
-
59
- // Try to keep the domain
60
- try {
61
- const parsed = new URL(url);
62
- const domain = parsed.host;
63
- const path = parsed.pathname + parsed.search;
64
-
65
- if (domain.length + 3 >= maxLength) {
66
- return url.slice(0, maxLength - 1) + "…";
67
- }
68
-
69
- const remaining = maxLength - domain.length - 3;
70
- if (path.length <= remaining) {
71
- return domain + path;
72
- }
73
-
74
- return domain + path.slice(0, remaining - 1) + "…";
75
- } catch {
76
- return url.slice(0, maxLength - 1) + "…";
77
- }
78
- }
79
-
80
- /**
81
- * Render a single progress item line.
82
- *
83
- * @param progress - Progress object
84
- * @param width - Available width
85
- * @param spinnerIndex - Animation frame index
86
- * @returns Formatted line
87
- */
88
- export function renderProgressLine(
89
- progress: FetchProgress,
90
- width: number = 80,
91
- spinnerIndex: number = 0
92
- ): string {
93
- // Status glyph
94
- let glyph = STATUS_GLYPHS[progress.status];
95
- if (["connecting", "waiting", "loading", "processing"].includes(progress.status)) {
96
- glyph = getSpinnerFrame(spinnerIndex);
97
- }
98
-
99
- // Truncate URL
100
- const urlMax = Math.min(40, width - 30);
101
- const url = truncateUrl(progress.url, urlMax);
102
-
103
- // Progress bar
104
- const bar = renderProgressBar(progress.percent, 8);
105
-
106
- // Status text
107
- const statusText = progress.phase || progress.status;
108
-
109
- // Format line
110
- return `${glyph} ${url.padEnd(urlMax)} ${statusText.padEnd(12)} [${bar}]`;
111
- }
112
-
113
- /**
114
- * Render batch progress header.
115
- *
116
- * @param progress - All progress items
117
- * @param concurrency - Current concurrency
118
- * @returns Header line
119
- */
120
- export function renderBatchProgressHeader(
121
- progress: FetchProgress[],
122
- concurrency: number
123
- ): string {
124
- const total = progress.length;
125
- const done = progress.filter((p) => p.status === "done").length;
126
- const error = progress.filter((p) => p.status === "error").length;
127
- const active = progress.filter(
128
- (p) => !["queued", "done", "error"].includes(p.status)
129
- ).length;
130
-
131
- return `batch_web_content_read ${done}/${total} done · ok ${done - error} · err ${error} · concurrency ${concurrency}`;
132
- }
133
-
134
- /**
135
- * Render full batch progress display.
136
- *
137
- * @param progress - All progress items
138
- * @param concurrency - Current concurrency
139
- * @param width - Available width
140
- * @param spinnerIndex - Animation frame index
141
- * @returns Formatted string
142
- */
143
- export function renderBatchProgress(
144
- progress: FetchProgress[],
145
- concurrency: number = 8,
146
- width: number = 80,
147
- spinnerIndex: number = 0
148
- ): string {
149
- const lines: string[] = [];
150
-
151
- // Header
152
- lines.push(renderBatchProgressHeader(progress, concurrency));
153
- lines.push("");
154
-
155
- // Progress items (show up to 10)
156
- const maxItems = 10;
157
- const itemsToShow = progress.slice(0, maxItems);
158
-
159
- for (const item of itemsToShow) {
160
- lines.push(renderProgressLine(item, width, spinnerIndex));
161
- }
162
-
163
- if (progress.length > maxItems) {
164
- lines.push(` ... and ${progress.length - maxItems} more`);
165
- }
166
-
167
- return lines.join("\n");
168
- }
package/src/tui/result.ts DELETED
@@ -1,173 +0,0 @@
1
- /**
2
- * @unipi/web-api — TUI Result Renderer
3
- *
4
- * Renders single and batch results for TUI display.
5
- */
6
-
7
- import type { FetchResult, BatchFetchResult, FetchError } from "../engine/types.js";
8
-
9
- /** Maximum preview lines */
10
- const PREVIEW_LINES = 7;
11
-
12
- /**
13
- * Truncate content for preview.
14
- *
15
- * @param content - Content to preview
16
- * @param maxLines - Maximum lines
17
- * @returns Preview string
18
- */
19
- function truncatePreview(content: string, maxLines: number = PREVIEW_LINES): string {
20
- const lines = content.split("\n").slice(0, maxLines);
21
- return lines.join("\n");
22
- }
23
-
24
- /**
25
- * Render a single result for display.
26
- *
27
- * @param result - Fetch result
28
- * @param verbose - Include metadata header
29
- * @returns Formatted string
30
- */
31
- export function renderSingleResult(
32
- result: FetchResult,
33
- verbose: boolean = true
34
- ): string {
35
- const lines: string[] = [];
36
-
37
- if (verbose) {
38
- // Title
39
- lines.push(`# ${result.title || "Untitled"}`);
40
- lines.push("");
41
-
42
- // Metadata
43
- const meta: string[] = [];
44
- if (result.author) {
45
- meta.push(`Author: ${result.author}`);
46
- }
47
- if (result.published) {
48
- meta.push(`Published: ${result.published}`);
49
- }
50
- if (result.site) {
51
- meta.push(`Site: ${result.site}`);
52
- }
53
- if (result.language) {
54
- meta.push(`Language: ${result.language}`);
55
- }
56
- if (result.wordCount) {
57
- meta.push(`Words: ${result.wordCount}`);
58
- }
59
-
60
- if (meta.length > 0) {
61
- lines.push(meta.join(" · "));
62
- }
63
-
64
- // URL
65
- lines.push(`URL: ${result.url}`);
66
- if (result.finalUrl !== result.url) {
67
- lines.push(`Final URL: ${result.finalUrl}`);
68
- }
69
-
70
- lines.push("");
71
- lines.push("---");
72
- lines.push("");
73
- }
74
-
75
- // Content preview
76
- const preview = truncatePreview(result.content);
77
- lines.push(preview);
78
-
79
- // Expand hint
80
- if (result.content.split("\n").length > PREVIEW_LINES) {
81
- lines.push("");
82
- lines.push(`... [${result.wordCount} words total · Ctrl+O to expand]`);
83
- }
84
-
85
- return lines.join("\n");
86
- }
87
-
88
- /**
89
- * Render a batch result for display.
90
- *
91
- * @param result - Batch fetch result
92
- * @returns Formatted string
93
- */
94
- export function renderBatchResult(result: BatchFetchResult): string {
95
- const lines: string[] = [];
96
-
97
- // Summary header
98
- lines.push(`# Batch Read Results`);
99
- lines.push("");
100
- lines.push(
101
- `Total: ${result.total} · Succeeded: ${result.succeeded} · Failed: ${result.failed}`
102
- );
103
- lines.push("");
104
-
105
- // Per-item results
106
- for (let i = 0; i < result.items.length; i++) {
107
- const item = result.items[i];
108
- const status = item.status === "done" ? "✓" : "✗";
109
-
110
- lines.push(`## [${i + 1}/${result.total}] ${status}`);
111
-
112
- if (item.status === "done") {
113
- lines.push(`**${item.result.title}**`);
114
- lines.push(`URL: ${item.result.url}`);
115
- lines.push(`Words: ${item.result.wordCount}`);
116
- lines.push("");
117
-
118
- // Content preview
119
- const preview = truncatePreview(item.result.content);
120
- lines.push(preview);
121
-
122
- if (item.result.content.split("\n").length > PREVIEW_LINES) {
123
- lines.push("...");
124
- }
125
- } else {
126
- lines.push(`URL: ${item.error.url || "unknown"}`);
127
- lines.push(`Error: ${item.error.error}`);
128
- }
129
-
130
- lines.push("");
131
- }
132
-
133
- return lines.join("\n");
134
- }
135
-
136
- /**
137
- * Render an error result for display.
138
- *
139
- * @param error - Fetch error
140
- * @returns Formatted string
141
- */
142
- export function renderErrorResult(error: FetchError): string {
143
- const lines: string[] = [];
144
-
145
- lines.push(`# Fetch Error`);
146
- lines.push("");
147
- lines.push(`**${error.error}**`);
148
- lines.push("");
149
- lines.push(`Code: \`${error.code}\``);
150
- lines.push(`Phase: \`${error.phase}\``);
151
-
152
- if (error.url) {
153
- lines.push("");
154
- lines.push(`URL: ${error.url}`);
155
- if (error.finalUrl && error.finalUrl !== error.url) {
156
- lines.push(`Final URL: ${error.finalUrl}`);
157
- }
158
- }
159
-
160
- if (error.statusCode) {
161
- lines.push("");
162
- lines.push(
163
- `HTTP Status: ${error.statusCode}${error.statusText ? ` ${error.statusText}` : ""}`
164
- );
165
- }
166
-
167
- if (error.retryable) {
168
- lines.push("");
169
- lines.push(`*This error may be retried.*`);
170
- }
171
-
172
- return lines.join("\n");
173
- }