@longtable/cli 0.1.58 → 0.1.60
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/dist/cli.js +358 -30
- package/dist/debate.js +19 -31
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/longtable-codex-native-hook.js +36 -8
- package/dist/panel.d.ts +13 -1
- package/dist/panel.js +339 -27
- package/dist/project-session.js +88 -5
- package/dist/search/index.d.ts +1 -6
- package/dist/search/index.js +1 -6
- package/package.json +8 -7
- package/dist/search/publisher-access.d.ts +0 -21
- package/dist/search/publisher-access.js +0 -564
- package/dist/search/query.d.ts +0 -6
- package/dist/search/query.js +0 -179
- package/dist/search/rank.d.ts +0 -2
- package/dist/search/rank.js +0 -173
- package/dist/search/run.d.ts +0 -2
- package/dist/search/run.js +0 -114
- package/dist/search/sources.d.ts +0 -5
- package/dist/search/sources.js +0 -537
- package/dist/search/types.d.ts +0 -181
- package/dist/search/types.js +0 -16
package/dist/search/query.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { SEARCH_SOURCES } from "./types.js";
|
|
2
|
-
const STOP_WORDS = new Set([
|
|
3
|
-
"a",
|
|
4
|
-
"about",
|
|
5
|
-
"an",
|
|
6
|
-
"and",
|
|
7
|
-
"are",
|
|
8
|
-
"as",
|
|
9
|
-
"at",
|
|
10
|
-
"be",
|
|
11
|
-
"before",
|
|
12
|
-
"between",
|
|
13
|
-
"but",
|
|
14
|
-
"by",
|
|
15
|
-
"can",
|
|
16
|
-
"do",
|
|
17
|
-
"does",
|
|
18
|
-
"for",
|
|
19
|
-
"from",
|
|
20
|
-
"has",
|
|
21
|
-
"have",
|
|
22
|
-
"how",
|
|
23
|
-
"in",
|
|
24
|
-
"into",
|
|
25
|
-
"is",
|
|
26
|
-
"it",
|
|
27
|
-
"its",
|
|
28
|
-
"of",
|
|
29
|
-
"on",
|
|
30
|
-
"or",
|
|
31
|
-
"should",
|
|
32
|
-
"study",
|
|
33
|
-
"that",
|
|
34
|
-
"the",
|
|
35
|
-
"their",
|
|
36
|
-
"this",
|
|
37
|
-
"to",
|
|
38
|
-
"what",
|
|
39
|
-
"when",
|
|
40
|
-
"where",
|
|
41
|
-
"whether",
|
|
42
|
-
"which",
|
|
43
|
-
"with"
|
|
44
|
-
]);
|
|
45
|
-
function searchId() {
|
|
46
|
-
return `search_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
47
|
-
}
|
|
48
|
-
export function normalizeSearchText(value) {
|
|
49
|
-
return value
|
|
50
|
-
.toLowerCase()
|
|
51
|
-
.replace(/https?:\/\/\S+/g, " ")
|
|
52
|
-
.replace(/doi:\s*/g, "doi ")
|
|
53
|
-
.replace(/[^a-z0-9가-힣._:/-]+/gi, " ")
|
|
54
|
-
.replace(/\s+/g, " ")
|
|
55
|
-
.trim();
|
|
56
|
-
}
|
|
57
|
-
export function splitCsvTerms(value) {
|
|
58
|
-
if (!value) {
|
|
59
|
-
return [];
|
|
60
|
-
}
|
|
61
|
-
return value
|
|
62
|
-
.split(",")
|
|
63
|
-
.map((entry) => normalizeSearchText(entry))
|
|
64
|
-
.filter(Boolean);
|
|
65
|
-
}
|
|
66
|
-
export function extractSearchKeywords(text, limit = 12) {
|
|
67
|
-
const normalized = normalizeSearchText(text);
|
|
68
|
-
const counts = new Map();
|
|
69
|
-
for (const token of normalized.split(" ")) {
|
|
70
|
-
if (token.length < 3 || STOP_WORDS.has(token)) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
74
|
-
}
|
|
75
|
-
return [...counts.entries()]
|
|
76
|
-
.sort((a, b) => {
|
|
77
|
-
const countDelta = b[1] - a[1];
|
|
78
|
-
return countDelta !== 0 ? countDelta : a[0].localeCompare(b[0]);
|
|
79
|
-
})
|
|
80
|
-
.slice(0, limit)
|
|
81
|
-
.map(([token]) => token);
|
|
82
|
-
}
|
|
83
|
-
export function parseSearchSources(value) {
|
|
84
|
-
if (!value || value === "all") {
|
|
85
|
-
return [...SEARCH_SOURCES];
|
|
86
|
-
}
|
|
87
|
-
const requested = value
|
|
88
|
-
.split(",")
|
|
89
|
-
.map((entry) => entry.trim())
|
|
90
|
-
.filter(Boolean);
|
|
91
|
-
const sources = [];
|
|
92
|
-
for (const source of requested) {
|
|
93
|
-
if (!SEARCH_SOURCES.includes(source)) {
|
|
94
|
-
throw new Error(`Unknown search source: ${source}`);
|
|
95
|
-
}
|
|
96
|
-
sources.push(source);
|
|
97
|
-
}
|
|
98
|
-
return sources.length > 0 ? sources : [...SEARCH_SOURCES];
|
|
99
|
-
}
|
|
100
|
-
function inferIntentKind(text, explicit) {
|
|
101
|
-
if (explicit) {
|
|
102
|
-
if (explicit === "literature" ||
|
|
103
|
-
explicit === "theory" ||
|
|
104
|
-
explicit === "measurement" ||
|
|
105
|
-
explicit === "citation" ||
|
|
106
|
-
explicit === "metadata" ||
|
|
107
|
-
explicit === "venue") {
|
|
108
|
-
return explicit;
|
|
109
|
-
}
|
|
110
|
-
throw new Error(`Unknown search intent: ${explicit}`);
|
|
111
|
-
}
|
|
112
|
-
const normalized = normalizeSearchText(text);
|
|
113
|
-
if (/\b(citation|reference|doi|source|hallucination|verify|support)\b|인용|레퍼런스|출처|근거/.test(normalized)) {
|
|
114
|
-
return "citation";
|
|
115
|
-
}
|
|
116
|
-
if (/\b(scale|measure|measurement|instrument|validity|reliability)\b|측정|척도|타당도|도구/.test(normalized)) {
|
|
117
|
-
return "measurement";
|
|
118
|
-
}
|
|
119
|
-
if (/\b(theory|theoretical|framework|construct|conceptual)\b|이론|개념|프레임워크/.test(normalized)) {
|
|
120
|
-
return "theory";
|
|
121
|
-
}
|
|
122
|
-
if (/\b(journal|venue|conference|submission|scope|fit)\b|저널|학회|투고/.test(normalized)) {
|
|
123
|
-
return "venue";
|
|
124
|
-
}
|
|
125
|
-
if (/\b(metadata|pmid|arxiv|openalex|semantic scholar)\b/.test(normalized)) {
|
|
126
|
-
return "metadata";
|
|
127
|
-
}
|
|
128
|
-
return "literature";
|
|
129
|
-
}
|
|
130
|
-
function buildQueryVariants(baseQuery, keywords, field, mustTerms = []) {
|
|
131
|
-
const variants = new Set();
|
|
132
|
-
variants.add(baseQuery);
|
|
133
|
-
const compact = [...mustTerms, ...keywords].slice(0, 8).join(" ");
|
|
134
|
-
if (compact) {
|
|
135
|
-
variants.add(compact);
|
|
136
|
-
}
|
|
137
|
-
if (field && compact) {
|
|
138
|
-
variants.add(`${field} ${compact}`);
|
|
139
|
-
}
|
|
140
|
-
return [...variants].filter(Boolean).slice(0, 3);
|
|
141
|
-
}
|
|
142
|
-
export function buildResearchSearchIntent(input) {
|
|
143
|
-
const explicitQuery = input.query?.trim();
|
|
144
|
-
const baseText = explicitQuery || [
|
|
145
|
-
input.prompt,
|
|
146
|
-
input.projectGoal,
|
|
147
|
-
input.projectBlocker
|
|
148
|
-
]
|
|
149
|
-
.filter((entry) => Boolean(entry && entry.trim()))
|
|
150
|
-
.join(" ");
|
|
151
|
-
if (!baseText.trim()) {
|
|
152
|
-
throw new Error("A search query is required. Pass --query or run inside a workspace with a current goal.");
|
|
153
|
-
}
|
|
154
|
-
const mustTerms = splitCsvTerms(input.must);
|
|
155
|
-
const excludeTerms = splitCsvTerms(input.exclude);
|
|
156
|
-
const field = input.field?.trim() || undefined;
|
|
157
|
-
const keywordText = [baseText, field, ...mustTerms].filter(Boolean).join(" ");
|
|
158
|
-
const keywords = extractSearchKeywords(keywordText);
|
|
159
|
-
const normalizedQuery = normalizeSearchText(baseText);
|
|
160
|
-
const query = keywords.length > 0 ? keywords.slice(0, 10).join(" ") : normalizedQuery;
|
|
161
|
-
const limit = Number.isInteger(input.limit) && input.limit && input.limit > 0
|
|
162
|
-
? Math.min(input.limit, 50)
|
|
163
|
-
: 10;
|
|
164
|
-
return {
|
|
165
|
-
id: searchId(),
|
|
166
|
-
createdAt: new Date().toISOString(),
|
|
167
|
-
kind: inferIntentKind(baseText, input.intent),
|
|
168
|
-
query,
|
|
169
|
-
normalizedQuery,
|
|
170
|
-
queryVariants: buildQueryVariants(query, keywords, field, mustTerms),
|
|
171
|
-
keywords,
|
|
172
|
-
...(field ? { field } : {}),
|
|
173
|
-
mustTerms,
|
|
174
|
-
excludeTerms,
|
|
175
|
-
requestedSources: parseSearchSources(input.sources),
|
|
176
|
-
limit,
|
|
177
|
-
source: input.source ?? "cli"
|
|
178
|
-
};
|
|
179
|
-
}
|
package/dist/search/rank.d.ts
DELETED
package/dist/search/rank.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { normalizeSearchText } from "./query.js";
|
|
2
|
-
function keyForCard(card) {
|
|
3
|
-
if (card.doi)
|
|
4
|
-
return `doi:${card.doi.toLowerCase()}`;
|
|
5
|
-
if (card.pmid)
|
|
6
|
-
return `pmid:${card.pmid}`;
|
|
7
|
-
if (card.arxivId)
|
|
8
|
-
return `arxiv:${card.arxivId.toLowerCase()}`;
|
|
9
|
-
if (card.openAlexId)
|
|
10
|
-
return `openalex:${card.openAlexId}`;
|
|
11
|
-
if (card.semanticScholarId)
|
|
12
|
-
return `s2:${card.semanticScholarId}`;
|
|
13
|
-
const title = normalizeSearchText(card.title).replace(/\s+/g, "-");
|
|
14
|
-
return `title:${title}:${card.year ?? "unknown"}`;
|
|
15
|
-
}
|
|
16
|
-
function keywordMatches(card, keywords) {
|
|
17
|
-
const haystack = normalizeSearchText([
|
|
18
|
-
card.title,
|
|
19
|
-
card.abstract,
|
|
20
|
-
card.venue,
|
|
21
|
-
card.researchDesign,
|
|
22
|
-
card.mainFinding
|
|
23
|
-
].filter(Boolean).join(" "));
|
|
24
|
-
return keywords.filter((keyword) => haystack.includes(normalizeSearchText(keyword)));
|
|
25
|
-
}
|
|
26
|
-
function sourceBoost(card) {
|
|
27
|
-
if (card.sourceRoutes.includes("openalex"))
|
|
28
|
-
return 6;
|
|
29
|
-
if (card.sourceRoutes.includes("semantic_scholar"))
|
|
30
|
-
return 5;
|
|
31
|
-
if (card.sourceRoutes.includes("pubmed"))
|
|
32
|
-
return 5;
|
|
33
|
-
if (card.sourceRoutes.includes("crossref"))
|
|
34
|
-
return 4;
|
|
35
|
-
if (card.sourceRoutes.includes("arxiv"))
|
|
36
|
-
return 3;
|
|
37
|
-
if (card.sourceRoutes.includes("doaj"))
|
|
38
|
-
return 3;
|
|
39
|
-
if (card.sourceRoutes.includes("eric"))
|
|
40
|
-
return 3;
|
|
41
|
-
return 1;
|
|
42
|
-
}
|
|
43
|
-
function supportStatus(card, matches, keywords) {
|
|
44
|
-
if (card.verificationDepth === "metadata" || !card.abstractAvailable) {
|
|
45
|
-
return "not_verified";
|
|
46
|
-
}
|
|
47
|
-
const ratio = keywords.length === 0 ? 0 : matches.length / keywords.length;
|
|
48
|
-
const fullTextChecked = card.verificationDepth === "licensed_snippet" || card.verificationDepth === "legal_full_text";
|
|
49
|
-
if (fullTextChecked && ratio >= 0.65)
|
|
50
|
-
return "direct_support";
|
|
51
|
-
if (ratio >= 0.35)
|
|
52
|
-
return "indirect_support";
|
|
53
|
-
if (ratio > 0)
|
|
54
|
-
return "background";
|
|
55
|
-
return "questionable_fit";
|
|
56
|
-
}
|
|
57
|
-
function scoreCard(card, intent, matches) {
|
|
58
|
-
const title = normalizeSearchText(card.title);
|
|
59
|
-
const abstract = normalizeSearchText(card.abstract ?? "");
|
|
60
|
-
let score = 0;
|
|
61
|
-
for (const keyword of intent.keywords) {
|
|
62
|
-
const normalized = normalizeSearchText(keyword);
|
|
63
|
-
if (title.includes(normalized))
|
|
64
|
-
score += 8;
|
|
65
|
-
if (abstract.includes(normalized))
|
|
66
|
-
score += 3;
|
|
67
|
-
}
|
|
68
|
-
for (const term of intent.mustTerms) {
|
|
69
|
-
const normalized = normalizeSearchText(term);
|
|
70
|
-
if (title.includes(normalized) || abstract.includes(normalized)) {
|
|
71
|
-
score += 10;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
score -= 20;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
for (const term of intent.excludeTerms) {
|
|
78
|
-
const normalized = normalizeSearchText(term);
|
|
79
|
-
if (title.includes(normalized) || abstract.includes(normalized)) {
|
|
80
|
-
score -= 30;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (card.year) {
|
|
84
|
-
const age = Math.max(0, new Date().getUTCFullYear() - card.year);
|
|
85
|
-
score += Math.max(0, 10 - Math.min(age, 10));
|
|
86
|
-
}
|
|
87
|
-
if (card.citationCount) {
|
|
88
|
-
score += Math.min(12, Math.log10(card.citationCount + 1) * 4);
|
|
89
|
-
}
|
|
90
|
-
if (card.legalFullTextAvailable)
|
|
91
|
-
score += 4;
|
|
92
|
-
score += sourceBoost(card);
|
|
93
|
-
score += matches.length * 2;
|
|
94
|
-
return Math.max(0, Math.round(score * 10) / 10);
|
|
95
|
-
}
|
|
96
|
-
function accessStrength(card) {
|
|
97
|
-
if (card.accessStatus === "licensed_full_text_checked")
|
|
98
|
-
return 6;
|
|
99
|
-
if (card.accessStatus === "licensed_full_text_available")
|
|
100
|
-
return 5;
|
|
101
|
-
if (card.accessStatus === "legal_full_text_available")
|
|
102
|
-
return 4;
|
|
103
|
-
if (card.accessStatus === "abstract_available")
|
|
104
|
-
return 3;
|
|
105
|
-
if (card.accessStatus === "metadata_only")
|
|
106
|
-
return 2;
|
|
107
|
-
if (card.accessStatus === "license_unknown")
|
|
108
|
-
return 1;
|
|
109
|
-
return 0;
|
|
110
|
-
}
|
|
111
|
-
function strongerAccess(existing, incoming) {
|
|
112
|
-
return accessStrength(incoming) > accessStrength(existing) ? incoming : existing;
|
|
113
|
-
}
|
|
114
|
-
function mergeCards(existing, incoming) {
|
|
115
|
-
const sourceRoutes = [...new Set([...existing.sourceRoutes, ...incoming.sourceRoutes])];
|
|
116
|
-
const stronger = strongerAccess(existing, incoming);
|
|
117
|
-
return {
|
|
118
|
-
...existing,
|
|
119
|
-
authors: existing.authors.length > 0 ? existing.authors : incoming.authors,
|
|
120
|
-
year: existing.year ?? incoming.year,
|
|
121
|
-
venue: existing.venue ?? incoming.venue,
|
|
122
|
-
doi: existing.doi ?? incoming.doi,
|
|
123
|
-
pmid: existing.pmid ?? incoming.pmid,
|
|
124
|
-
arxivId: existing.arxivId ?? incoming.arxivId,
|
|
125
|
-
openAlexId: existing.openAlexId ?? incoming.openAlexId,
|
|
126
|
-
semanticScholarId: existing.semanticScholarId ?? incoming.semanticScholarId,
|
|
127
|
-
ericId: existing.ericId ?? incoming.ericId,
|
|
128
|
-
url: existing.url ?? incoming.url,
|
|
129
|
-
abstract: existing.abstract ?? incoming.abstract,
|
|
130
|
-
abstractAvailable: existing.abstractAvailable || incoming.abstractAvailable,
|
|
131
|
-
legalFullTextAvailable: existing.legalFullTextAvailable || incoming.legalFullTextAvailable,
|
|
132
|
-
fullTextUrl: existing.fullTextUrl ?? incoming.fullTextUrl,
|
|
133
|
-
publisher: existing.publisher ?? incoming.publisher,
|
|
134
|
-
entitlementSource: stronger.entitlementSource ?? existing.entitlementSource ?? incoming.entitlementSource,
|
|
135
|
-
collectionDepth: stronger.collectionDepth ?? existing.collectionDepth ?? incoming.collectionDepth,
|
|
136
|
-
licenseNote: stronger.licenseNote ?? existing.licenseNote ?? incoming.licenseNote,
|
|
137
|
-
publisherAccess: stronger.publisherAccess ?? existing.publisherAccess ?? incoming.publisherAccess,
|
|
138
|
-
accessStatus: stronger.accessStatus,
|
|
139
|
-
verificationDepth: stronger.verificationDepth,
|
|
140
|
-
verificationNote: stronger.verificationNote,
|
|
141
|
-
citationCount: Math.max(existing.citationCount ?? 0, incoming.citationCount ?? 0) || undefined,
|
|
142
|
-
researchDesign: existing.researchDesign ?? incoming.researchDesign,
|
|
143
|
-
constructsOrMeasures: [...new Set([...(existing.constructsOrMeasures ?? []), ...(incoming.constructsOrMeasures ?? [])])],
|
|
144
|
-
mainFinding: existing.mainFinding ?? incoming.mainFinding,
|
|
145
|
-
sourceRoutes,
|
|
146
|
-
limitations: [...new Set([...existing.limitations, ...incoming.limitations])],
|
|
147
|
-
matchedKeywords: [...new Set([...existing.matchedKeywords, ...incoming.matchedKeywords])],
|
|
148
|
-
relevanceScore: Math.max(existing.relevanceScore, incoming.relevanceScore)
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
export function dedupeAndRankCards(cards, intent) {
|
|
152
|
-
const byKey = new Map();
|
|
153
|
-
for (const card of cards) {
|
|
154
|
-
const matches = keywordMatches(card, intent.keywords);
|
|
155
|
-
const scored = {
|
|
156
|
-
...card,
|
|
157
|
-
matchedKeywords: matches,
|
|
158
|
-
citationSupportStatus: supportStatus(card, matches, intent.keywords),
|
|
159
|
-
relevanceScore: scoreCard(card, intent, matches)
|
|
160
|
-
};
|
|
161
|
-
const key = keyForCard(scored);
|
|
162
|
-
const existing = byKey.get(key);
|
|
163
|
-
byKey.set(key, existing ? mergeCards(existing, scored) : scored);
|
|
164
|
-
}
|
|
165
|
-
return [...byKey.values()]
|
|
166
|
-
.sort((a, b) => {
|
|
167
|
-
const scoreDelta = b.relevanceScore - a.relevanceScore;
|
|
168
|
-
if (scoreDelta !== 0)
|
|
169
|
-
return scoreDelta;
|
|
170
|
-
return (b.year ?? 0) - (a.year ?? 0);
|
|
171
|
-
})
|
|
172
|
-
.slice(0, intent.limit);
|
|
173
|
-
}
|
package/dist/search/run.d.ts
DELETED
package/dist/search/run.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { buildResearchSearchIntent } from "./query.js";
|
|
2
|
-
import { dedupeAndRankCards } from "./rank.js";
|
|
3
|
-
import { enrichCardsWithPublisherAccess } from "./publisher-access.js";
|
|
4
|
-
import { assessSearchSourceCapabilities, runSourceSearch } from "./sources.js";
|
|
5
|
-
function runId() {
|
|
6
|
-
return `evidence_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
7
|
-
}
|
|
8
|
-
function now() {
|
|
9
|
-
return new Date().toISOString();
|
|
10
|
-
}
|
|
11
|
-
function defaultFetch() {
|
|
12
|
-
if (typeof fetch !== "function") {
|
|
13
|
-
throw new Error("LongTable search requires a fetch-capable Node runtime.");
|
|
14
|
-
}
|
|
15
|
-
return fetch;
|
|
16
|
-
}
|
|
17
|
-
export async function runResearchSearch(input) {
|
|
18
|
-
const createdAt = now();
|
|
19
|
-
const id = runId();
|
|
20
|
-
const intent = buildResearchSearchIntent(input);
|
|
21
|
-
const env = input.env ?? process.env;
|
|
22
|
-
const capabilities = assessSearchSourceCapabilities(intent.requestedSources, env);
|
|
23
|
-
const skippedSources = capabilities.filter((capability) => !capability.enabled);
|
|
24
|
-
if (skippedSources.length > 0 && input.allowPartial !== true) {
|
|
25
|
-
const updatedAt = now();
|
|
26
|
-
return {
|
|
27
|
-
id,
|
|
28
|
-
createdAt,
|
|
29
|
-
updatedAt,
|
|
30
|
-
status: "blocked",
|
|
31
|
-
intent,
|
|
32
|
-
sourceReports: skippedSources.map((capability) => ({
|
|
33
|
-
source: capability.source,
|
|
34
|
-
status: "skipped",
|
|
35
|
-
count: 0,
|
|
36
|
-
elapsedMs: 0,
|
|
37
|
-
reason: capability.reason
|
|
38
|
-
})),
|
|
39
|
-
cards: [],
|
|
40
|
-
skippedSources,
|
|
41
|
-
warnings: skippedSources.map((capability) => capability.reason ?? `${capability.source} unavailable.`),
|
|
42
|
-
blockedReason: "One or more requested scholarly sources are unavailable. Confirm partial search or configure credentials."
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const httpFetch = input.fetch ?? defaultFetch();
|
|
46
|
-
const sourceReports = [];
|
|
47
|
-
const cards = [];
|
|
48
|
-
for (const capability of capabilities) {
|
|
49
|
-
if (!capability.enabled) {
|
|
50
|
-
sourceReports.push({
|
|
51
|
-
source: capability.source,
|
|
52
|
-
status: "skipped",
|
|
53
|
-
count: 0,
|
|
54
|
-
elapsedMs: 0,
|
|
55
|
-
reason: capability.reason
|
|
56
|
-
});
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const started = Date.now();
|
|
60
|
-
try {
|
|
61
|
-
const result = await runSourceSearch({
|
|
62
|
-
intent,
|
|
63
|
-
source: capability.source,
|
|
64
|
-
limit: intent.limit
|
|
65
|
-
}, {
|
|
66
|
-
fetch: httpFetch,
|
|
67
|
-
env
|
|
68
|
-
});
|
|
69
|
-
cards.push(...result.cards);
|
|
70
|
-
sourceReports.push({
|
|
71
|
-
source: capability.source,
|
|
72
|
-
status: "completed",
|
|
73
|
-
count: result.cards.length,
|
|
74
|
-
elapsedMs: Date.now() - started,
|
|
75
|
-
endpoint: result.endpoint
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
sourceReports.push({
|
|
80
|
-
source: capability.source,
|
|
81
|
-
status: "failed",
|
|
82
|
-
count: 0,
|
|
83
|
-
elapsedMs: Date.now() - started,
|
|
84
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const rankedCards = dedupeAndRankCards(cards, intent);
|
|
89
|
-
const finalCards = input.publisherAccess === true
|
|
90
|
-
? await enrichCardsWithPublisherAccess({
|
|
91
|
-
cards: rankedCards,
|
|
92
|
-
env,
|
|
93
|
-
fetch: httpFetch
|
|
94
|
-
})
|
|
95
|
-
: rankedCards;
|
|
96
|
-
const hasFailure = sourceReports.some((report) => report.status === "failed" || report.status === "skipped");
|
|
97
|
-
const status = hasFailure ? "partial" : "completed";
|
|
98
|
-
return {
|
|
99
|
-
id,
|
|
100
|
-
createdAt,
|
|
101
|
-
updatedAt: now(),
|
|
102
|
-
status,
|
|
103
|
-
intent,
|
|
104
|
-
sourceReports,
|
|
105
|
-
cards: finalCards,
|
|
106
|
-
skippedSources,
|
|
107
|
-
warnings: [
|
|
108
|
-
...skippedSources.map((capability) => capability.reason ?? `${capability.source} unavailable.`),
|
|
109
|
-
...sourceReports
|
|
110
|
-
.filter((report) => report.status === "failed")
|
|
111
|
-
.map((report) => `${report.source} failed: ${report.reason ?? "unknown error"}`)
|
|
112
|
-
]
|
|
113
|
-
};
|
|
114
|
-
}
|
package/dist/search/sources.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type SearchSource, type SearchSourceCapability, type SourceSearchContext, type SourceSearchRequest, type SourceSearchResult } from "./types.js";
|
|
2
|
-
export declare function assessSearchSourceCapabilities(sources: SearchSource[], env?: Record<string, string | undefined>): SearchSourceCapability[];
|
|
3
|
-
export declare function enabledSearchSources(sources: SearchSource[], env?: Record<string, string | undefined>): SearchSource[];
|
|
4
|
-
export declare function runSourceSearch(request: SourceSearchRequest, context: SourceSearchContext): Promise<SourceSearchResult>;
|
|
5
|
-
export declare function allSearchSources(): SearchSource[];
|