@hraness/wordcell 0.25.0 → 0.26.0
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 +37 -35
- package/dist/capture.js +6 -6
- package/dist/cli-bin.js +14 -0
- package/dist/cli-release-c74c5m3k.js +799 -0
- package/dist/cli.js +26 -23
- package/dist/clip/acquire.js +3 -3
- package/dist/clip/cli.js +6 -6
- package/dist/clip/doctor.js +4 -4
- package/dist/evaluation-builder-bin.js +14 -0
- package/dist/evaluation-builder.js +12 -17
- package/dist/evaluation-kb.js +6 -6
- package/dist/graph-percolation.js +2 -2
- package/dist/index-3t0v457d.js +18 -0
- package/dist/{index-47893rgw.js → index-5h5awwh4.js} +4 -4
- package/dist/index-8pabwzqg.js +909 -0
- package/dist/{index-1wccy765.js → index-8w1zsgcm.js} +63 -930
- package/dist/{index-7drercgk.js → index-9fywek7g.js} +1 -1
- package/dist/{index-hz2wyggk.js → index-9paxge84.js} +1 -1
- package/dist/{index-mcpppwbq.js → index-9zkba8hr.js} +6 -6
- package/dist/{index-59bcxqee.js → index-j2jt49gr.js} +1 -1
- package/dist/index-ncgdaja4.js +64 -0
- package/dist/{index-nwrehke7.js → index-nd8v7r0z.js} +1 -1
- package/dist/{index-t3abkwk4.js → index-zbv70qmk.js} +15 -15
- package/dist/index.js +40 -40
- package/dist/pdf.js +2 -2
- package/dist/portfolio.js +5 -5
- package/dist/sdk.js +4 -4
- package/dist/{support-adj7bbge.js → support-ym4mpf7w.js} +19 -18
- package/dist/url-intelligence.js +3 -3
- package/dist/workflows/decision-context.js +5 -5
- package/dist/workflows/index.js +5 -5
- package/package.json +13 -6
- package/skills/wordcell/SKILL.md +10 -1
- package/src/cli-bin.ts +5 -0
- package/src/cli-help.ts +12 -0
- package/src/cli-program.ts +3 -57
- package/src/cli-release.ts +732 -0
- package/src/cli-startup.ts +76 -0
- package/src/cli-update.ts +53 -0
- package/src/cli.ts +5 -4
- package/src/clip/metadata-search-tool/src/openalex.rs +156 -8
- package/src/evaluation-builder-bin.ts +5 -0
- package/src/evaluation-builder-cli-args.ts +30 -0
- package/src/evaluation-builder.ts +3 -27
- package/src/import-supermemory.ts +3 -1
- package/dist/{index-hxyjer3x.js → index-hacpnaew.js} +3 -3
- package/dist/{index-qfqjfxaa.js → index-m9p2a4mj.js} +4 -4
- package/dist/{index-xwxy71ew.js → index-ncwzmsge.js} +5 -5
- package/dist/{index-e26mdwxz.js → index-wqx1x32f.js} +3 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { commandWordCount, resolveCommandId, valueOptions } from "./cli-help.js";
|
|
2
|
+
|
|
3
|
+
export type StartupCommand =
|
|
4
|
+
| { readonly kind: "help"; readonly topic?: string }
|
|
5
|
+
| { readonly kind: "version"; readonly json: boolean }
|
|
6
|
+
| { readonly kind: "clip"; readonly arguments: readonly string[] }
|
|
7
|
+
| { readonly kind: "pdf"; readonly arguments: readonly string[] }
|
|
8
|
+
| { readonly kind: "url-metadata"; readonly arguments: readonly string[] };
|
|
9
|
+
export type StartupParseResult =
|
|
10
|
+
| { readonly ok: true; readonly value: StartupCommand }
|
|
11
|
+
| { readonly ok: false; readonly message: string };
|
|
12
|
+
|
|
13
|
+
function isHelpFlag(argument: string | undefined): boolean {
|
|
14
|
+
return argument === "--help" || argument === "-h";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Help for delegated commands comes from their own parsers. */
|
|
18
|
+
function delegatedHelp(first: string): StartupCommand | undefined {
|
|
19
|
+
if (first === "clip" || first === "inspect") return { kind: "clip", arguments: ["help"] };
|
|
20
|
+
if (first === "pdf") return { kind: "pdf", arguments: ["--help"] };
|
|
21
|
+
if (first === "url-metadata") return { kind: "url-metadata", arguments: ["--help"] };
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** `help`, `help advanced`, and `help <command...>`. */
|
|
26
|
+
function parseHelpTopic(words: readonly string[]): StartupParseResult {
|
|
27
|
+
const topic = words.filter((word) => !isHelpFlag(word) && word !== "--json");
|
|
28
|
+
if (topic.length === 0) return { ok: true, value: { kind: "help" } };
|
|
29
|
+
if (topic.length === 1 && topic[0] === "advanced") return { ok: true, value: { kind: "help", topic: "advanced" } };
|
|
30
|
+
const delegated = delegatedHelp(topic[0] ?? "");
|
|
31
|
+
if (delegated !== undefined) return { ok: true, value: delegated };
|
|
32
|
+
const id = resolveCommandId(topic);
|
|
33
|
+
if (id === undefined) return { ok: false, message: "unknown help topic" };
|
|
34
|
+
return { ok: true, value: { kind: "help", topic: id } };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `<command> --help` anywhere a person would type it: right after the command
|
|
39
|
+
* words, or as the last argument when it is not the value of an option.
|
|
40
|
+
*/
|
|
41
|
+
function embeddedHelp(arguments_: readonly string[]): StartupParseResult | undefined {
|
|
42
|
+
const separator = arguments_.indexOf("--");
|
|
43
|
+
const end = separator === -1 ? arguments_.length : separator;
|
|
44
|
+
const index = arguments_.slice(0, end).findIndex(isHelpFlag);
|
|
45
|
+
if (index <= 0) return undefined;
|
|
46
|
+
const delegated = delegatedHelp(arguments_[0] ?? "");
|
|
47
|
+
if (delegated !== undefined) {
|
|
48
|
+
return index === 1 || index === end - 1 ? { ok: true, value: delegated } : undefined;
|
|
49
|
+
}
|
|
50
|
+
const id = resolveCommandId(arguments_.slice(0, index).filter((word) => !word.startsWith("-")));
|
|
51
|
+
if (id === undefined) return undefined;
|
|
52
|
+
const words = commandWordCount(id);
|
|
53
|
+
const previous = arguments_[index - 1] ?? "";
|
|
54
|
+
// `--help` is never an option value; `-h` could be one, as in `--body -h`.
|
|
55
|
+
const flag = arguments_[index];
|
|
56
|
+
const lastArgument = index === end - 1 && (flag === "--help" || !valueOptions(id).has(previous));
|
|
57
|
+
if (index !== words && !lastArgument) return undefined;
|
|
58
|
+
return { ok: true, value: { kind: "help", topic: id } };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The executable and public parser share the same help/version grammar. */
|
|
62
|
+
export function parseCliStartupCommand(arguments_: readonly string[]): StartupParseResult | undefined {
|
|
63
|
+
const command = arguments_[0];
|
|
64
|
+
if (command === undefined) return { ok: true, value: { kind: "help", topic: "start" } };
|
|
65
|
+
if (command === "help" || isHelpFlag(command)) return parseHelpTopic(arguments_.slice(1));
|
|
66
|
+
if (command === "--version" || command === "-V" || command === "-v" || command === "version") {
|
|
67
|
+
return { ok: true, value: { kind: "version", json: arguments_.includes("--json") } };
|
|
68
|
+
}
|
|
69
|
+
const help = embeddedHelp(arguments_);
|
|
70
|
+
if (help !== undefined) return help;
|
|
71
|
+
if ((command === "clip" || command === "capture" || command === "inspect")
|
|
72
|
+
&& ["--help", "-h", "help"].includes(arguments_[1] ?? "")) {
|
|
73
|
+
return { ok: true, value: { kind: "clip", arguments: ["help"] } };
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { runCliUpdate, type CliUpdateOptions } from "@hraness/cli-update";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { findKbPackageRoot } from "./clip/package-root.js";
|
|
5
|
+
import { parseCliStartupCommand } from "./cli-startup.js";
|
|
6
|
+
import { isEvaluationBuilderHelp } from "./evaluation-builder-cli-args.js";
|
|
7
|
+
|
|
8
|
+
export type WordcellBin = "wordcell" | "wordcell-evaluation-builder";
|
|
9
|
+
|
|
10
|
+
export function wordcellUpdateOptions(bin: WordcellBin, entrypoint: string, argv: readonly string[]): CliUpdateOptions {
|
|
11
|
+
const packageRoot = findKbPackageRoot(dirname(entrypoint));
|
|
12
|
+
const manifest: unknown = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
|
13
|
+
if (manifest === null || typeof manifest !== "object" || !("version" in manifest) || typeof manifest.version !== "string") {
|
|
14
|
+
throw new Error("Wordcell package version is missing.");
|
|
15
|
+
}
|
|
16
|
+
const effectFree = bin === "wordcell" ? parseCliStartupCommand(argv) !== undefined : isEvaluationBuilderHelp(argv);
|
|
17
|
+
return {
|
|
18
|
+
packageName: "@hraness/wordcell", version: manifest.version, binName: bin, entrypoint,
|
|
19
|
+
argv: effectFree && argv[0] === "update" ? ["--help"] : argv,
|
|
20
|
+
provider: { kind: "github", repository: "hraness/wordcell", tagPrefix: "v", assetName: "hraness-wordcell-{version}.tgz" },
|
|
21
|
+
ignoreScripts: true,
|
|
22
|
+
effectFree,
|
|
23
|
+
suppressAutomatic: bin === "wordcell" && argv[0] === "support",
|
|
24
|
+
async verifyArtifact(artifact) {
|
|
25
|
+
const { verifyUpdateArtifact } = await import("./cli-release.js");
|
|
26
|
+
await verifyUpdateArtifact(artifact);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Admission precedes imports of vault, capture, support, or evaluation code. */
|
|
32
|
+
export async function runWordcellBin(
|
|
33
|
+
bin: WordcellBin,
|
|
34
|
+
entrypoint: string,
|
|
35
|
+
argv: readonly string[],
|
|
36
|
+
dependencies: { update?: typeof runCliUpdate; run?: () => Promise<number> } = {},
|
|
37
|
+
): Promise<number> {
|
|
38
|
+
const update = await (dependencies.update ?? runCliUpdate)(wordcellUpdateOptions(bin, entrypoint, argv));
|
|
39
|
+
if (update.handled) return update.exitCode;
|
|
40
|
+
try {
|
|
41
|
+
if (dependencies.run) return await dependencies.run();
|
|
42
|
+
if (bin === "wordcell") {
|
|
43
|
+
const { runStandaloneCli } = await import("./cli.js");
|
|
44
|
+
return await runStandaloneCli(argv);
|
|
45
|
+
}
|
|
46
|
+
const { runKbEvidenceRoutingBuildCli, kbEvidenceRoutingBuildUsage } = await import("./evaluation-builder.js");
|
|
47
|
+
if (isEvaluationBuilderHelp(argv)) console.log(kbEvidenceRoutingBuildUsage);
|
|
48
|
+
else await runKbEvidenceRoutingBuildCli(argv);
|
|
49
|
+
return 0;
|
|
50
|
+
} finally {
|
|
51
|
+
await update.release();
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -3,15 +3,16 @@ import { runExecutable } from "./cli-program.js";
|
|
|
3
3
|
|
|
4
4
|
export * from "./cli-program.js";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export async function runStandaloneCli(args: readonly string[] = process.argv.slice(2)): Promise<number> {
|
|
7
7
|
const { standaloneSupportEnvironment, isUsefulSupportResult, runProductSupportCommand, showProductSupportInvitation } = await import("./support.js");
|
|
8
8
|
const env = standaloneSupportEnvironment();
|
|
9
|
-
const args = process.argv.slice(2);
|
|
10
9
|
if (args[0] === "support") {
|
|
11
|
-
|
|
10
|
+
return await runProductSupportCommand(args.slice(1), { env });
|
|
12
11
|
} else {
|
|
13
12
|
const exitCode = await runExecutable(args);
|
|
14
|
-
process.exitCode = exitCode;
|
|
15
13
|
if (exitCode === 0 && isUsefulSupportResult(args, env)) await showProductSupportInvitation({ env });
|
|
14
|
+
return exitCode;
|
|
16
15
|
}
|
|
17
16
|
}
|
|
17
|
+
|
|
18
|
+
if (import.meta.main) process.exitCode = await runStandaloneCli();
|
|
@@ -170,8 +170,8 @@ fn parse(body: &str, limit: usize, query: &str) -> Outcome {
|
|
|
170
170
|
Some(cost)
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
|
-
let mut
|
|
174
|
-
let mut
|
|
173
|
+
let mut known_retracted_ids = std::collections::HashSet::new();
|
|
174
|
+
let mut observed_ids = Vec::with_capacity(rows.len());
|
|
175
175
|
let mut results = Vec::new();
|
|
176
176
|
for row in rows {
|
|
177
177
|
let id = row
|
|
@@ -191,6 +191,18 @@ fn parse(body: &str, limit: usize, query: &str) -> Outcome {
|
|
|
191
191
|
if title.trim().is_empty() || title.len() > MAX_TEXT {
|
|
192
192
|
return Err(FailureCode::Parse);
|
|
193
193
|
}
|
|
194
|
+
let title = title.split_whitespace().collect::<Vec<_>>().join(" ");
|
|
195
|
+
// Absence is unknown; only an explicit flag or title marker excludes a work.
|
|
196
|
+
let retracted = match row.get("is_retracted") {
|
|
197
|
+
None => false,
|
|
198
|
+
Some(value) => value.as_bool().ok_or(FailureCode::Parse)?,
|
|
199
|
+
};
|
|
200
|
+
const RETRACTED_PREFIX: &str = "RETRACTED ARTICLE:";
|
|
201
|
+
let retracted = retracted
|
|
202
|
+
|| title
|
|
203
|
+
.get(..RETRACTED_PREFIX.len())
|
|
204
|
+
.is_some_and(|prefix| prefix.eq_ignore_ascii_case(RETRACTED_PREFIX));
|
|
205
|
+
observed_ids.push(id);
|
|
194
206
|
let mut candidates = Vec::new();
|
|
195
207
|
let mut locations = Vec::new();
|
|
196
208
|
for field in ["best_oa_location", "primary_location"] {
|
|
@@ -270,21 +282,41 @@ fn parse(body: &str, limit: usize, query: &str) -> Outcome {
|
|
|
270
282
|
}
|
|
271
283
|
_ => return Err(FailureCode::Parse),
|
|
272
284
|
}
|
|
285
|
+
// Validate every row's bounded fields even when its work will be omitted.
|
|
286
|
+
if retracted {
|
|
287
|
+
known_retracted_ids.insert(id);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
273
290
|
// Prefer observed direct destinations over DOI redirectors; preserve stable location order.
|
|
274
291
|
candidates.sort_by_key(|(priority, _)| *priority);
|
|
275
292
|
let Some((_, url)) = candidates.into_iter().next() else {
|
|
276
293
|
continue;
|
|
277
294
|
};
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
295
|
+
results.push((
|
|
296
|
+
id,
|
|
297
|
+
SearchResult {
|
|
298
|
+
title,
|
|
281
299
|
url,
|
|
282
300
|
snippet: Some("Scholarly index metadata; full text not fetched.".into()),
|
|
283
301
|
source_engine: "openalex".into(),
|
|
284
|
-
}
|
|
285
|
-
|
|
302
|
+
},
|
|
303
|
+
));
|
|
286
304
|
}
|
|
287
|
-
|
|
305
|
+
// A duplicate row cannot reintroduce an identity explicitly marked retracted.
|
|
306
|
+
// Deduplicate only admitted works so excluded identities cannot hide usable rows.
|
|
307
|
+
let mut seen = std::collections::HashSet::new();
|
|
308
|
+
let mut seen_ids = std::collections::HashSet::new();
|
|
309
|
+
let results: Vec<_> = results
|
|
310
|
+
.into_iter()
|
|
311
|
+
.filter(|(id, _)| !known_retracted_ids.contains(id))
|
|
312
|
+
.filter(|(id, result)| seen_ids.insert(*id) && seen.insert(result.url.clone()))
|
|
313
|
+
.map(|(_, result)| result)
|
|
314
|
+
.collect();
|
|
315
|
+
if results.is_empty()
|
|
316
|
+
&& observed_ids
|
|
317
|
+
.iter()
|
|
318
|
+
.any(|id| !known_retracted_ids.contains(id))
|
|
319
|
+
{
|
|
288
320
|
return Err(FailureCode::UnverifiedEmpty);
|
|
289
321
|
}
|
|
290
322
|
Ok(Discovery {
|
|
@@ -307,6 +339,122 @@ mod tests {
|
|
|
307
339
|
parse(&value.to_string(), 3, "microalgae")
|
|
308
340
|
}
|
|
309
341
|
#[test]
|
|
342
|
+
fn omits_known_retractions_and_preserves_cost_for_policy_empty_results() {
|
|
343
|
+
let mut retracted = row();
|
|
344
|
+
retracted["is_retracted"] = json!(true);
|
|
345
|
+
let mut value = response(vec![retracted.clone()]);
|
|
346
|
+
value["meta"]["cost_usd"] = json!(0.0001);
|
|
347
|
+
let result = check(value).unwrap();
|
|
348
|
+
assert!(result.results.is_empty());
|
|
349
|
+
assert_eq!(result.reported_cost_usd, Some(0.0001));
|
|
350
|
+
|
|
351
|
+
let mut good = row();
|
|
352
|
+
good["id"] = json!("https://openalex.org/W456");
|
|
353
|
+
good["is_retracted"] = json!(false);
|
|
354
|
+
let result = check(response(vec![retracted, good])).unwrap();
|
|
355
|
+
assert_eq!(result.results.len(), 1);
|
|
356
|
+
assert_eq!(
|
|
357
|
+
result.results[0].title,
|
|
358
|
+
"Microalgae harvesting energy efficiency"
|
|
359
|
+
);
|
|
360
|
+
// Missing status remains admissible metadata, without a verified-status claim.
|
|
361
|
+
let result = check(response(vec![row()])).unwrap();
|
|
362
|
+
assert_eq!(result.results.len(), 1);
|
|
363
|
+
assert_eq!(
|
|
364
|
+
result.results[0].snippet.as_deref(),
|
|
365
|
+
Some("Scholarly index metadata; full text not fetched.")
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
#[test]
|
|
369
|
+
fn retraction_marker_is_anchored_and_does_not_exclude_scholarship_about_retractions() {
|
|
370
|
+
for title in [
|
|
371
|
+
"RETRACTED ARTICLE: Microalgae",
|
|
372
|
+
" retracted article:Microalgae",
|
|
373
|
+
"RETRACTED\n ARTICLE: Microalgae",
|
|
374
|
+
] {
|
|
375
|
+
let mut r = row();
|
|
376
|
+
r["title"] = json!(title);
|
|
377
|
+
r["is_retracted"] = json!(false);
|
|
378
|
+
assert!(
|
|
379
|
+
check(response(vec![r])).unwrap().results.is_empty(),
|
|
380
|
+
"{title}"
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
for title in [
|
|
384
|
+
"A study of retractions",
|
|
385
|
+
"Analysis of RETRACTED ARTICLE: labels",
|
|
386
|
+
"RETRACTED ARTICLES: a review",
|
|
387
|
+
"Retraction notices in science",
|
|
388
|
+
"論文について",
|
|
389
|
+
] {
|
|
390
|
+
let mut r = row();
|
|
391
|
+
r["title"] = json!(title);
|
|
392
|
+
assert_eq!(
|
|
393
|
+
check(response(vec![r])).unwrap().results.len(),
|
|
394
|
+
1,
|
|
395
|
+
"{title}"
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
#[test]
|
|
400
|
+
fn rejects_malformed_status_even_with_an_explicit_title_marker() {
|
|
401
|
+
for status in [Value::Null, json!("true"), json!(1), json!([]), json!({})] {
|
|
402
|
+
for title in ["Microalgae harvesting", "RETRACTED ARTICLE: Microalgae"] {
|
|
403
|
+
let mut r = row();
|
|
404
|
+
r["title"] = json!(title);
|
|
405
|
+
r["is_retracted"] = status.clone();
|
|
406
|
+
assert_eq!(check(response(vec![r])).unwrap_err(), FailureCode::Parse);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
#[test]
|
|
411
|
+
fn policy_filter_preserves_unusable_link_failures_and_row_validation() {
|
|
412
|
+
let mut retracted = row();
|
|
413
|
+
retracted["is_retracted"] = json!(true);
|
|
414
|
+
let mut unusable = row();
|
|
415
|
+
unusable["id"] = json!("https://openalex.org/W456");
|
|
416
|
+
unusable["best_oa_location"] = Value::Null;
|
|
417
|
+
unusable["doi"] = Value::Null;
|
|
418
|
+
assert_eq!(
|
|
419
|
+
check(response(vec![retracted.clone(), unusable])).unwrap_err(),
|
|
420
|
+
FailureCode::UnverifiedEmpty
|
|
421
|
+
);
|
|
422
|
+
retracted["doi"] = json!(42);
|
|
423
|
+
assert_eq!(
|
|
424
|
+
check(response(vec![retracted.clone()])).unwrap_err(),
|
|
425
|
+
FailureCode::Parse
|
|
426
|
+
);
|
|
427
|
+
retracted["doi"] = Value::Null;
|
|
428
|
+
retracted["locations"] = json!([{"is_oa":true,"landing_page_url":"x".repeat(8193)}]);
|
|
429
|
+
assert_eq!(
|
|
430
|
+
check(response(vec![retracted])).unwrap_err(),
|
|
431
|
+
FailureCode::BodyLimit
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
#[test]
|
|
435
|
+
fn duplicate_work_cannot_reintroduce_a_known_retraction_in_either_order() {
|
|
436
|
+
let mut retracted = row();
|
|
437
|
+
retracted["is_retracted"] = json!(true);
|
|
438
|
+
for rows in [vec![row(), retracted.clone()], vec![retracted, row()]] {
|
|
439
|
+
assert!(check(response(rows)).unwrap().results.is_empty());
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
#[test]
|
|
443
|
+
fn excluded_identity_cannot_consume_a_valid_works_url_deduplication_slot() {
|
|
444
|
+
let mut retracted = row();
|
|
445
|
+
retracted["is_retracted"] = json!(true);
|
|
446
|
+
let mut good = row();
|
|
447
|
+
good["id"] = json!("https://openalex.org/W456");
|
|
448
|
+
good["title"] = json!("Allowed work at the shared destination");
|
|
449
|
+
let result = check(response(vec![row(), good, retracted])).unwrap();
|
|
450
|
+
assert_eq!(result.results.len(), 1);
|
|
451
|
+
assert_eq!(
|
|
452
|
+
result.results[0].title,
|
|
453
|
+
"Allowed work at the shared destination"
|
|
454
|
+
);
|
|
455
|
+
assert_eq!(result.results[0].url, "https://repository.example/paper");
|
|
456
|
+
}
|
|
457
|
+
#[test]
|
|
310
458
|
fn discovery_prefers_observed_oa_landing_and_deduplicates() {
|
|
311
459
|
let results = check(response(vec![row(), row()])).unwrap().results;
|
|
312
460
|
assert_eq!(results.len(), 1);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const kbEvidenceRoutingBuildUsage =
|
|
2
|
+
"Usage: wordcell-evaluation-builder <--anchor-seal|--build> --config <checked-config.json> --artifact-root <artifact-B>\n"
|
|
3
|
+
+ " wordcell-evaluation-builder update [check|status|enable|disable] [--json]";
|
|
4
|
+
|
|
5
|
+
export function parseKbEvidenceRoutingBuildCliArguments(arguments_: readonly string[]): {
|
|
6
|
+
readonly mode: "anchor-seal" | "build";
|
|
7
|
+
readonly configPath: string;
|
|
8
|
+
readonly artifactRoot: string;
|
|
9
|
+
} {
|
|
10
|
+
const mode = arguments_[0];
|
|
11
|
+
const configPath = arguments_[2];
|
|
12
|
+
const artifactRoot = arguments_[4];
|
|
13
|
+
if (
|
|
14
|
+
arguments_.length !== 5
|
|
15
|
+
|| (mode !== "--anchor-seal" && mode !== "--build")
|
|
16
|
+
|| arguments_[1] !== "--config"
|
|
17
|
+
|| arguments_[3] !== "--artifact-root"
|
|
18
|
+
|| configPath === undefined
|
|
19
|
+
|| configPath.trim() === ""
|
|
20
|
+
|| artifactRoot === undefined
|
|
21
|
+
|| artifactRoot.trim() === ""
|
|
22
|
+
) {
|
|
23
|
+
throw new Error(kbEvidenceRoutingBuildUsage);
|
|
24
|
+
}
|
|
25
|
+
return Object.freeze({ mode: mode.slice(2) as "anchor-seal" | "build", configPath, artifactRoot });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isEvaluationBuilderHelp(arguments_: readonly string[]): boolean {
|
|
29
|
+
return arguments_.length === 1 && (arguments_[0] === "--help" || arguments_[0] === "-h");
|
|
30
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { isEvaluationBuilderHelp, kbEvidenceRoutingBuildUsage, parseKbEvidenceRoutingBuildCliArguments } from "./evaluation-builder-cli-args.js";
|
|
3
|
+
export { kbEvidenceRoutingBuildUsage, parseKbEvidenceRoutingBuildCliArguments } from "./evaluation-builder-cli-args.js";
|
|
2
4
|
|
|
3
5
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
6
|
import { constants as fsConstants } from "node:fs";
|
|
@@ -2459,32 +2461,6 @@ export async function buildKbEvidenceRoutingEvaluation(
|
|
|
2459
2461
|
};
|
|
2460
2462
|
}
|
|
2461
2463
|
|
|
2462
|
-
export const kbEvidenceRoutingBuildUsage =
|
|
2463
|
-
"Usage: wordcell-evaluation-builder <--anchor-seal|--build> --config <checked-config.json> --artifact-root <artifact-B>";
|
|
2464
|
-
|
|
2465
|
-
export function parseKbEvidenceRoutingBuildCliArguments(arguments_: readonly string[]): {
|
|
2466
|
-
readonly mode: "anchor-seal" | "build";
|
|
2467
|
-
readonly configPath: string;
|
|
2468
|
-
readonly artifactRoot: string;
|
|
2469
|
-
} {
|
|
2470
|
-
const mode = arguments_[0];
|
|
2471
|
-
const configPath = arguments_[2];
|
|
2472
|
-
const artifactRoot = arguments_[4];
|
|
2473
|
-
if (
|
|
2474
|
-
arguments_.length !== 5
|
|
2475
|
-
|| (mode !== "--anchor-seal" && mode !== "--build")
|
|
2476
|
-
|| arguments_[1] !== "--config"
|
|
2477
|
-
|| arguments_[3] !== "--artifact-root"
|
|
2478
|
-
|| configPath === undefined
|
|
2479
|
-
|| configPath.trim() === ""
|
|
2480
|
-
|| artifactRoot === undefined
|
|
2481
|
-
|| artifactRoot.trim() === ""
|
|
2482
|
-
) {
|
|
2483
|
-
throw new Error(kbEvidenceRoutingBuildUsage);
|
|
2484
|
-
}
|
|
2485
|
-
return Object.freeze({ mode: mode.slice(2) as "anchor-seal" | "build", configPath, artifactRoot });
|
|
2486
|
-
}
|
|
2487
|
-
|
|
2488
2464
|
export async function runKbEvidenceRoutingBuildCli(
|
|
2489
2465
|
arguments_: readonly string[],
|
|
2490
2466
|
dependencies: Pick<KbEvidenceRoutingEvaluationBuildDependencies, "runGit"> = {},
|
|
@@ -2518,7 +2494,7 @@ export async function runKbEvidenceRoutingBuildCli(
|
|
|
2518
2494
|
|
|
2519
2495
|
if (import.meta.main) {
|
|
2520
2496
|
const arguments_ = process.argv.slice(2);
|
|
2521
|
-
if (
|
|
2497
|
+
if (isEvaluationBuilderHelp(arguments_)) {
|
|
2522
2498
|
console.log(kbEvidenceRoutingBuildUsage);
|
|
2523
2499
|
} else {
|
|
2524
2500
|
await runKbEvidenceRoutingBuildCli(arguments_);
|
|
@@ -828,7 +828,9 @@ export function occupancyKey(value: string): string {
|
|
|
828
828
|
|
|
829
829
|
/** Validate `--prefix` as a vault-relative directory note-ID prefix. */
|
|
830
830
|
export function normalizePrefix(prefix: string): string {
|
|
831
|
-
|
|
831
|
+
let end = prefix.length;
|
|
832
|
+
while (end > 0 && prefix.charCodeAt(end - 1) === 47) end -= 1;
|
|
833
|
+
const trimmed = prefix.slice(0, end);
|
|
832
834
|
let canonical: string | undefined;
|
|
833
835
|
try {
|
|
834
836
|
canonical = canonicalNoteId(trimmed);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
sanitizeArtifactUrl
|
|
4
|
+
} from "./index-mxxxytys.js";
|
|
2
5
|
import {
|
|
3
6
|
assertSafeNetworkUrl,
|
|
4
7
|
decodeBytes,
|
|
5
8
|
isPrivateHostname,
|
|
6
9
|
safeFetch
|
|
7
10
|
} from "./index-e5fbsywq.js";
|
|
8
|
-
import {
|
|
9
|
-
sanitizeArtifactUrl
|
|
10
|
-
} from "./index-mxxxytys.js";
|
|
11
11
|
|
|
12
12
|
// src/clip/url-intelligence.ts
|
|
13
13
|
var MAX_METADATA_SEARCH_RESULTS = 20;
|
|
@@ -11,16 +11,16 @@ import {
|
|
|
11
11
|
terminalOutput,
|
|
12
12
|
yamlString
|
|
13
13
|
} from "./index-cd75vky9.js";
|
|
14
|
+
import {
|
|
15
|
+
redactSensitiveText,
|
|
16
|
+
sanitizeArtifactUrl
|
|
17
|
+
} from "./index-mxxxytys.js";
|
|
14
18
|
import {
|
|
15
19
|
safeFetch
|
|
16
20
|
} from "./index-e5fbsywq.js";
|
|
17
21
|
import {
|
|
18
22
|
BoundedByteBuffer
|
|
19
23
|
} from "./index-gh719d91.js";
|
|
20
|
-
import {
|
|
21
|
-
redactSensitiveText,
|
|
22
|
-
sanitizeArtifactUrl
|
|
23
|
-
} from "./index-mxxxytys.js";
|
|
24
24
|
import {
|
|
25
25
|
sanitizeTerminalLine,
|
|
26
26
|
sanitizeTerminalText
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
readCookieFile,
|
|
9
9
|
renderCookieHeader
|
|
10
10
|
} from "./index-2gv8y733.js";
|
|
11
|
+
import {
|
|
12
|
+
sanitizeArtifactUrl
|
|
13
|
+
} from "./index-mxxxytys.js";
|
|
11
14
|
import {
|
|
12
15
|
startNetworkProxy
|
|
13
16
|
} from "./index-w2zc0vwa.js";
|
|
@@ -16,15 +19,12 @@ import {
|
|
|
16
19
|
decodeBytes,
|
|
17
20
|
safeFetch
|
|
18
21
|
} from "./index-e5fbsywq.js";
|
|
19
|
-
import {
|
|
20
|
-
captureUrl
|
|
21
|
-
} from "./index-byz4kzww.js";
|
|
22
22
|
import {
|
|
23
23
|
readBoundedByteStream
|
|
24
24
|
} from "./index-gh719d91.js";
|
|
25
25
|
import {
|
|
26
|
-
|
|
27
|
-
} from "./index-
|
|
26
|
+
captureUrl
|
|
27
|
+
} from "./index-byz4kzww.js";
|
|
28
28
|
import {
|
|
29
29
|
findKbPackageRoot,
|
|
30
30
|
resolvePackageDirectory
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import {
|
|
3
|
-
percolateVault
|
|
4
|
-
} from "./index-bdwcjvr4.js";
|
|
5
2
|
import {
|
|
6
3
|
createGraphSnapshot,
|
|
7
4
|
openGraphAuthority
|
|
@@ -10,6 +7,9 @@ import {
|
|
|
10
7
|
GraphAuthorityError,
|
|
11
8
|
validateGraphPercolationOptions
|
|
12
9
|
} from "./index-pgtm2nhf.js";
|
|
10
|
+
import {
|
|
11
|
+
percolateVault
|
|
12
|
+
} from "./index-bdwcjvr4.js";
|
|
13
13
|
import {
|
|
14
14
|
analyzeVault,
|
|
15
15
|
lookupNote,
|