@hraness/wordcell 0.24.1 → 0.25.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/README.md +4 -4
- package/dist/capture.js +2 -2
- package/dist/cli.js +5 -5
- package/dist/clip/cli.js +2 -2
- package/dist/clip/defuddle-worker.js +87 -2
- package/dist/{index-jqkkb4fr.js → index-1wccy765.js} +5 -5
- package/dist/{index-t0fh8a1q.js → index-47893rgw.js} +15 -5
- package/dist/{index-6jcz0m1c.js → index-hxyjer3x.js} +52 -1
- package/dist/{index-2fmnttca.js → index-t3abkwk4.js} +1 -1
- package/dist/{support-07jpt1z4.js → support-adj7bbge.js} +4 -4
- package/dist/url-intelligence.js +2 -2
- package/package.json +9 -7
- package/skills/wordcell/SKILL.md +1 -1
- package/src/cli-program.ts +3 -3
- package/src/clip/metadata-search-tool/Cargo.lock +10 -0
- package/src/clip/metadata-search-tool/Cargo.toml +1 -0
- package/src/clip/metadata-search-tool/src/bing.rs +539 -0
- package/src/clip/metadata-search-tool/src/duckduckgo.rs +61 -33
- package/src/clip/metadata-search-tool/src/main.rs +238 -12
- package/src/clip/metadata-search-tool/src/openalex.rs +705 -0
- package/src/clip/metadata-search.ts +18 -2
- package/src/clip/url-intelligence.ts +54 -1
- package/src/url-intelligence.ts +2 -0
|
@@ -92,6 +92,10 @@ export type SearchProvider = (request: SearchProviderRequest) => Promise<SearchP
|
|
|
92
92
|
export type RustMetadataSearchProviderOptions = {
|
|
93
93
|
/** Absolute path to the reviewed binary. The file identity is pinned when the provider is created. */
|
|
94
94
|
readonly binaryPath: string;
|
|
95
|
+
/** Opt in to the fixed, public Bing RSS endpoint. Disabled by default. */
|
|
96
|
+
readonly enableBingRss?: boolean;
|
|
97
|
+
/** Opt in to public OpenAlex scholarly discovery; indexed links are not fetched evidence. */
|
|
98
|
+
readonly enableOpenAlexDiscovery?: boolean;
|
|
95
99
|
readonly defaultMaxResults?: number;
|
|
96
100
|
readonly defaultTimeoutMs?: number;
|
|
97
101
|
readonly processGraceMs?: number;
|
|
@@ -483,14 +487,19 @@ function deterministicResponse(response: MetadataSearchResponse, maximumResults:
|
|
|
483
487
|
enginesQueried,
|
|
484
488
|
enginesFailed: Object.freeze([...response.enginesFailed].sort()),
|
|
485
489
|
engineStatus: response.engineStatus,
|
|
490
|
+
...(response.engineFailures === undefined ? {} : { engineFailures: Object.freeze(
|
|
491
|
+
[...response.engineFailures].sort((a, b) => compareText(a.engine, b.engine)),
|
|
492
|
+
) }),
|
|
486
493
|
});
|
|
487
494
|
}
|
|
488
495
|
|
|
489
496
|
async function resolveEngineHosts(
|
|
490
497
|
resolver: MetadataSearchNetworkResolver,
|
|
491
498
|
timeoutMs: number,
|
|
499
|
+
enableBingRss: boolean,
|
|
500
|
+
enableOpenAlexDiscovery: boolean,
|
|
492
501
|
): Promise<readonly ResolvedEngineHost[]> {
|
|
493
|
-
const resolved = await Promise.all(METADATA_SEARCH_ENGINE_HOSTS.map(async (hostname) => {
|
|
502
|
+
const resolved = await Promise.all([...METADATA_SEARCH_ENGINE_HOSTS, ...(enableBingRss ? ["www.bing.com"] : []), ...(enableOpenAlexDiscovery ? ["api.openalex.org"] : [])].map(async (hostname) => {
|
|
494
503
|
const addresses = await resolver(new URL(`https://${hostname}/`), {
|
|
495
504
|
allowPrivateNetwork: false,
|
|
496
505
|
timeoutMs,
|
|
@@ -548,6 +557,10 @@ export function createExactUrlSearchQuery(value: string | URL): string | null {
|
|
|
548
557
|
|
|
549
558
|
/** Create the isolated adapter for the reviewed Rust metadata-search helper. */
|
|
550
559
|
export function createRustMetadataSearchProvider(options: RustMetadataSearchProviderOptions): SearchProvider {
|
|
560
|
+
if (options.enableBingRss !== undefined && typeof options.enableBingRss !== "boolean") throw new TypeError("enableBingRss must be boolean.");
|
|
561
|
+
const enableBingRss = options.enableBingRss === true;
|
|
562
|
+
if (options.enableOpenAlexDiscovery !== undefined && typeof options.enableOpenAlexDiscovery !== "boolean") throw new TypeError("enableOpenAlexDiscovery must be boolean.");
|
|
563
|
+
const enableOpenAlexDiscovery = options.enableOpenAlexDiscovery === true;
|
|
551
564
|
if (!isAbsolute(options.binaryPath)) {
|
|
552
565
|
throw new TypeError("The metadata search binary path must be absolute.");
|
|
553
566
|
}
|
|
@@ -609,7 +622,7 @@ export function createRustMetadataSearchProvider(options: RustMetadataSearchProv
|
|
|
609
622
|
|
|
610
623
|
let engineHosts: readonly ResolvedEngineHost[];
|
|
611
624
|
try {
|
|
612
|
-
engineHosts = await resolveEngineHosts(resolveNetworkTarget, validated.timeoutMs);
|
|
625
|
+
engineHosts = await resolveEngineHosts(resolveNetworkTarget, validated.timeoutMs, enableBingRss, enableOpenAlexDiscovery);
|
|
613
626
|
} catch {
|
|
614
627
|
return failure("unavailable", "Metadata search network targets are unavailable.");
|
|
615
628
|
}
|
|
@@ -634,6 +647,9 @@ export function createRustMetadataSearchProvider(options: RustMetadataSearchProv
|
|
|
634
647
|
try {
|
|
635
648
|
const input = JSON.stringify({
|
|
636
649
|
schema_version: REQUEST_SCHEMA_VERSION,
|
|
650
|
+
diagnostics: true,
|
|
651
|
+
...(enableBingRss ? { bing_rss: true } : {}),
|
|
652
|
+
...(enableOpenAlexDiscovery ? { openalex_discovery: true } : {}),
|
|
637
653
|
query: validated.query,
|
|
638
654
|
max_results: validated.maxResults,
|
|
639
655
|
timeout_ms: validated.timeoutMs,
|
|
@@ -35,12 +35,33 @@ export type MetadataSearchResult = {
|
|
|
35
35
|
readonly score: number;
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
export const METADATA_SEARCH_FAILURE_CODES = [
|
|
39
|
+
"timeout", "http", "http-body", "http-status", "http-forbidden", "http-rate-limited", "http-redirect",
|
|
40
|
+
"parse", "unverified-empty", "challenge", "content-type", "content-encoding", "body-limit",
|
|
41
|
+
"body-encoding", "result-limit", "unrecognized-html", "query-mismatch",
|
|
42
|
+
] as const;
|
|
43
|
+
export type MetadataSearchFailureCode = (typeof METADATA_SEARCH_FAILURE_CODES)[number];
|
|
44
|
+
export type MetadataSearchEngineFailure = {
|
|
45
|
+
readonly engine: string;
|
|
46
|
+
readonly code: MetadataSearchFailureCode;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type MetadataSearchEngineUsage = {
|
|
50
|
+
readonly engine: "openalex";
|
|
51
|
+
readonly reportedCostUsd: number;
|
|
52
|
+
readonly billingStatus: "not-established";
|
|
53
|
+
};
|
|
54
|
+
|
|
38
55
|
export type MetadataSearchResponse = {
|
|
39
56
|
readonly query: string;
|
|
40
57
|
readonly results: readonly MetadataSearchResult[];
|
|
41
58
|
readonly enginesQueried: readonly string[];
|
|
42
59
|
readonly enginesFailed: readonly string[];
|
|
43
60
|
readonly engineStatus: MetadataSearchEngineStatus;
|
|
61
|
+
/** Absent for older providers; present entries cover every failed engine exactly once. */
|
|
62
|
+
readonly engineFailures?: readonly MetadataSearchEngineFailure[];
|
|
63
|
+
/** Provider-reported telemetry; this does not establish a user charge. */
|
|
64
|
+
readonly engineUsage?: readonly MetadataSearchEngineUsage[];
|
|
44
65
|
};
|
|
45
66
|
|
|
46
67
|
export type RankMetadataSearchOptions = {
|
|
@@ -246,7 +267,7 @@ function parseMetadataResult(
|
|
|
246
267
|
/** Strictly parse one successful metadata-search-engine-rs `GET /search` body. */
|
|
247
268
|
export function parseMetadataSearchResponse(value: unknown): MetadataSearchResponse {
|
|
248
269
|
const input = record(value, "Metadata search response");
|
|
249
|
-
strictKeys(input, ["query", "results", "engines_queried", "engines_failed"], "Metadata search response");
|
|
270
|
+
strictKeys(input, ["query", "results", "engines_queried", "engines_failed", "engine_failures", "engine_usage"], "Metadata search response");
|
|
250
271
|
const budget: TextBudget = { bytes: 0 };
|
|
251
272
|
const query = boundedString(input.query, "Metadata search response.query", MAX_METADATA_SEARCH_QUERY_UTF8_BYTES, budget);
|
|
252
273
|
if (query === "" || query !== query.trim() || hasUnsafeControls(query)) {
|
|
@@ -262,13 +283,45 @@ export function parseMetadataSearchResponse(value: unknown): MetadataSearchRespo
|
|
|
262
283
|
fail("Metadata search response.results", `must be an array with at most ${MAX_METADATA_SEARCH_RESULTS} entries.`);
|
|
263
284
|
}
|
|
264
285
|
const failed = new Set(enginesFailed);
|
|
286
|
+
let engineFailures: readonly MetadataSearchEngineFailure[] | undefined;
|
|
287
|
+
if (Object.hasOwn(input, "engine_failures")) {
|
|
288
|
+
if (!Array.isArray(input.engine_failures) || input.engine_failures.length !== enginesFailed.length) {
|
|
289
|
+
fail("Metadata search response.engine_failures", "must cover every failed engine exactly once.");
|
|
290
|
+
}
|
|
291
|
+
const seen = new Set<string>();
|
|
292
|
+
engineFailures = Object.freeze(input.engine_failures.map((value, index) => {
|
|
293
|
+
const label = `Metadata search response.engine_failures[${index}]`;
|
|
294
|
+
const entry = record(value, label);
|
|
295
|
+
strictKeys(entry, ["engine", "code"], label);
|
|
296
|
+
if (typeof entry.engine !== "string" || !failed.has(entry.engine) || seen.has(entry.engine)) {
|
|
297
|
+
fail(label, "must name a unique failed, queried engine.");
|
|
298
|
+
}
|
|
299
|
+
if (typeof entry.code !== "string" || !METADATA_SEARCH_FAILURE_CODES.some(code => code === entry.code)) {
|
|
300
|
+
fail(label, "must contain a known categorical failure code.");
|
|
301
|
+
}
|
|
302
|
+
seen.add(entry.engine);
|
|
303
|
+
return Object.freeze({ engine: entry.engine, code: entry.code as MetadataSearchFailureCode });
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
265
306
|
const results = Object.freeze(input.results.map((result, index) =>
|
|
266
307
|
parseMetadataResult(result, index, queried, failed, budget)));
|
|
308
|
+
let engineUsage: readonly MetadataSearchEngineUsage[] | undefined;
|
|
309
|
+
if (Object.hasOwn(input, "engine_usage")) {
|
|
310
|
+
if (!Array.isArray(input.engine_usage) || input.engine_usage.length !== 1) fail("Metadata search response.engine_usage", "must contain one bounded OpenAlex usage record.");
|
|
311
|
+
const entry = record(input.engine_usage[0], "engine usage");
|
|
312
|
+
strictKeys(entry, ["engine", "reported_cost_usd", "billing_status"], "engine usage");
|
|
313
|
+
if (entry.engine !== "openalex" || !enginesQueried.includes("openalex") || failed.has("openalex")
|
|
314
|
+
|| typeof entry.reported_cost_usd !== "number" || !Number.isFinite(entry.reported_cost_usd)
|
|
315
|
+
|| entry.reported_cost_usd < 0 || entry.reported_cost_usd > 1_000_000 || entry.billing_status !== "not-established") fail("engine usage", "must preserve bounded provider-reported usage without asserting billing.");
|
|
316
|
+
engineUsage = Object.freeze([Object.freeze({ engine: "openalex" as const, reportedCostUsd: entry.reported_cost_usd, billingStatus: "not-established" as const })]);
|
|
317
|
+
}
|
|
267
318
|
return Object.freeze({
|
|
268
319
|
query,
|
|
269
320
|
results,
|
|
270
321
|
enginesQueried,
|
|
271
322
|
enginesFailed,
|
|
323
|
+
...(engineFailures === undefined ? {} : { engineFailures }),
|
|
324
|
+
...(engineUsage === undefined ? {} : { engineUsage }),
|
|
272
325
|
engineStatus: enginesFailed.length === 0
|
|
273
326
|
? "complete"
|
|
274
327
|
: enginesFailed.length === enginesQueried.length ? "unavailable" : "partial",
|
package/src/url-intelligence.ts
CHANGED