@get-technology-inc/jamf-docs-mcp-server 0.0.1 → 1.1.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/LICENSE +21 -0
- package/README.md +111 -29
- package/dist/constants.d.ts +263 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +308 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.d.ts +13 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +45 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/schemas/index.d.ts +61 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +127 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/services/cache.d.ts +47 -0
- package/dist/services/cache.d.ts.map +1 -0
- package/dist/services/cache.js +165 -0
- package/dist/services/cache.js.map +1 -0
- package/dist/services/metadata.d.ts +78 -0
- package/dist/services/metadata.d.ts.map +1 -0
- package/dist/services/metadata.js +349 -0
- package/dist/services/metadata.js.map +1 -0
- package/dist/services/scraper.d.ts +62 -0
- package/dist/services/scraper.d.ts.map +1 -0
- package/dist/services/scraper.js +544 -0
- package/dist/services/scraper.js.map +1 -0
- package/dist/services/search-suggestions.d.ts +27 -0
- package/dist/services/search-suggestions.d.ts.map +1 -0
- package/dist/services/search-suggestions.js +193 -0
- package/dist/services/search-suggestions.js.map +1 -0
- package/dist/services/tokenizer.d.ts +93 -0
- package/dist/services/tokenizer.d.ts.map +1 -0
- package/dist/services/tokenizer.js +330 -0
- package/dist/services/tokenizer.js.map +1 -0
- package/dist/tools/get-article.d.ts +7 -0
- package/dist/tools/get-article.d.ts.map +1 -0
- package/dist/tools/get-article.js +244 -0
- package/dist/tools/get-article.js.map +1 -0
- package/dist/tools/get-toc.d.ts +7 -0
- package/dist/tools/get-toc.d.ts.map +1 -0
- package/dist/tools/get-toc.js +202 -0
- package/dist/tools/get-toc.js.map +1 -0
- package/dist/tools/list-products.d.ts +7 -0
- package/dist/tools/list-products.d.ts.map +1 -0
- package/dist/tools/list-products.js +150 -0
- package/dist/tools/list-products.js.map +1 -0
- package/dist/tools/search.d.ts +7 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +252 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/types.d.ts +188 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/services/metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web scraping service for Jamf documentation
|
|
3
|
+
*
|
|
4
|
+
* This module handles fetching and parsing HTML from learn.jamf.com
|
|
5
|
+
* (Jamf documentation has moved from docs.jamf.com to learn.jamf.com)
|
|
6
|
+
*/
|
|
7
|
+
import { type ProductId } from '../constants.js';
|
|
8
|
+
import { type SearchResult, type ParsedArticle, type TocEntry, type SearchParams, type TokenInfo, type PaginationInfo, type ArticleSection } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Search response with token and pagination info
|
|
11
|
+
*/
|
|
12
|
+
export interface SearchDocumentationResult {
|
|
13
|
+
results: SearchResult[];
|
|
14
|
+
pagination: PaginationInfo;
|
|
15
|
+
tokenInfo: TokenInfo;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Search Jamf documentation using Zoomin Search API
|
|
19
|
+
*/
|
|
20
|
+
export declare function searchDocumentation(params: SearchParams): Promise<SearchDocumentationResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Options for fetching articles
|
|
23
|
+
*/
|
|
24
|
+
export interface FetchArticleOptions {
|
|
25
|
+
includeRelated?: boolean;
|
|
26
|
+
section?: string;
|
|
27
|
+
summaryOnly?: boolean;
|
|
28
|
+
maxTokens?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Article result with token and section info
|
|
32
|
+
*/
|
|
33
|
+
export interface FetchArticleResult extends ParsedArticle {
|
|
34
|
+
tokenInfo: TokenInfo;
|
|
35
|
+
sections: ArticleSection[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fetch and parse a documentation article
|
|
39
|
+
* Uses backend URL (learn-be.jamf.com) for pre-rendered content
|
|
40
|
+
*/
|
|
41
|
+
export declare function fetchArticle(url: string, options?: FetchArticleOptions): Promise<FetchArticleResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Options for fetching table of contents
|
|
44
|
+
*/
|
|
45
|
+
export interface FetchTocOptions {
|
|
46
|
+
page?: number;
|
|
47
|
+
maxTokens?: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* TOC result with pagination and token info
|
|
51
|
+
*/
|
|
52
|
+
export interface FetchTocResult {
|
|
53
|
+
toc: TocEntry[];
|
|
54
|
+
pagination: PaginationInfo;
|
|
55
|
+
tokenInfo: TokenInfo;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Fetch table of contents for a product
|
|
59
|
+
* Uses backend TOC endpoint (learn-be.jamf.com/bundle/{bundleId}/toc)
|
|
60
|
+
*/
|
|
61
|
+
export declare function fetchTableOfContents(product: ProductId, version?: string, options?: FetchTocOptions): Promise<FetchTocResult>;
|
|
62
|
+
//# sourceMappingURL=scraper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scraper.d.ts","sourceRoot":"","sources":["../../src/services/scraper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAUL,KAAK,SAAS,EAEf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AA+LrB;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAiJlG;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAmH7B;AAoFD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;CACtB;AAkBD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,SAAS,EAClB,OAAO,SAAY,EACnB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CA+FzB"}
|
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web scraping service for Jamf documentation
|
|
3
|
+
*
|
|
4
|
+
* This module handles fetching and parsing HTML from learn.jamf.com
|
|
5
|
+
* (Jamf documentation has moved from docs.jamf.com to learn.jamf.com)
|
|
6
|
+
*/
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import * as cheerio from 'cheerio';
|
|
9
|
+
import TurndownService from 'turndown';
|
|
10
|
+
import { DOCS_BASE_URL, DOCS_API_URL, JAMF_PRODUCTS, JAMF_TOPICS, REQUEST_CONFIG, SELECTORS, CONTENT_LIMITS, TOKEN_CONFIG, PAGINATION_CONFIG } from '../constants.js';
|
|
11
|
+
import { JamfDocsError, JamfDocsErrorCode } from '../types.js';
|
|
12
|
+
import { cache } from './cache.js';
|
|
13
|
+
import { estimateTokens, createTokenInfo, extractSections, extractSection, extractSummary, truncateToTokenLimit, calculatePagination } from './tokenizer.js';
|
|
14
|
+
import { getBundleIdForVersion } from './metadata.js';
|
|
15
|
+
// Initialize Turndown for HTML to Markdown conversion
|
|
16
|
+
const turndown = new TurndownService({
|
|
17
|
+
headingStyle: 'atx',
|
|
18
|
+
codeBlockStyle: 'fenced',
|
|
19
|
+
bulletListMarker: '-',
|
|
20
|
+
emDelimiter: '*',
|
|
21
|
+
strongDelimiter: '**'
|
|
22
|
+
});
|
|
23
|
+
// Custom Turndown rules
|
|
24
|
+
turndown.addRule('codeBlocks', {
|
|
25
|
+
filter: 'pre',
|
|
26
|
+
replacement: (content, node) => {
|
|
27
|
+
// Handle code element extraction safely
|
|
28
|
+
const nodeElement = node;
|
|
29
|
+
const codeElement = nodeElement.querySelector?.('code');
|
|
30
|
+
const language = codeElement?.className?.replace('language-', '') ?? '';
|
|
31
|
+
return `\n\`\`\`${language}\n${content.trim()}\n\`\`\`\n`;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
// Rate limiter
|
|
35
|
+
let lastRequestTime = 0;
|
|
36
|
+
async function throttle() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const elapsed = now - lastRequestTime;
|
|
39
|
+
if (elapsed < REQUEST_CONFIG.RATE_LIMIT_DELAY) {
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, REQUEST_CONFIG.RATE_LIMIT_DELAY - elapsed));
|
|
41
|
+
}
|
|
42
|
+
lastRequestTime = Date.now();
|
|
43
|
+
}
|
|
44
|
+
// URL transformation between frontend (learn.jamf.com) and backend (learn-be.jamf.com)
|
|
45
|
+
const transformToBackendUrl = (url) => url.replace('learn.jamf.com', 'learn-be.jamf.com');
|
|
46
|
+
const transformToFrontendUrl = (url) => url.replace('learn-be.jamf.com', 'learn.jamf.com');
|
|
47
|
+
// HTML entity map for stripping HTML
|
|
48
|
+
const HTML_ENTITIES = {
|
|
49
|
+
' ': ' ', '&': '&', '<': '<', '>': '>', '"': '"', ''': "'"
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Strip HTML tags from a string
|
|
53
|
+
* Uses iterative approach to handle nested/malformed tags
|
|
54
|
+
*/
|
|
55
|
+
function stripHtml(html) {
|
|
56
|
+
let text = html;
|
|
57
|
+
// Iteratively remove HTML tags until none remain (handles nested cases like <scr<script>ipt>)
|
|
58
|
+
let prev = '';
|
|
59
|
+
while (prev !== text) {
|
|
60
|
+
prev = text;
|
|
61
|
+
text = text.replace(/<[^>]*>/g, '');
|
|
62
|
+
}
|
|
63
|
+
for (const [entity, char] of Object.entries(HTML_ENTITIES)) {
|
|
64
|
+
text = text.replaceAll(entity, char);
|
|
65
|
+
}
|
|
66
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Handle axios errors and convert to JamfDocsError
|
|
70
|
+
*/
|
|
71
|
+
function handleAxiosError(error, url, resourceType) {
|
|
72
|
+
const status = error.response?.status;
|
|
73
|
+
if (status === 404) {
|
|
74
|
+
throw new JamfDocsError(`${resourceType} not found: ${url}`, JamfDocsErrorCode.NOT_FOUND, url, 404);
|
|
75
|
+
}
|
|
76
|
+
if (status === 429) {
|
|
77
|
+
throw new JamfDocsError('Rate limited. Please wait and try again.', JamfDocsErrorCode.RATE_LIMITED, url, 429);
|
|
78
|
+
}
|
|
79
|
+
if (error.code === 'ECONNABORTED') {
|
|
80
|
+
throw new JamfDocsError('Request timed out', JamfDocsErrorCode.TIMEOUT, url);
|
|
81
|
+
}
|
|
82
|
+
throw new JamfDocsError(`Network error: ${error.message}`, JamfDocsErrorCode.NETWORK_ERROR, url);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Fetch data from a URL with error handling
|
|
86
|
+
*/
|
|
87
|
+
async function fetchUrl(url, accept, resourceType) {
|
|
88
|
+
await throttle();
|
|
89
|
+
try {
|
|
90
|
+
const response = await axios.get(url, {
|
|
91
|
+
timeout: REQUEST_CONFIG.TIMEOUT,
|
|
92
|
+
headers: {
|
|
93
|
+
'User-Agent': REQUEST_CONFIG.USER_AGENT,
|
|
94
|
+
'Accept': accept,
|
|
95
|
+
'Accept-Language': 'en-US,en;q=0.9'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (axios.isAxiosError(error)) {
|
|
102
|
+
handleAxiosError(error, url, resourceType);
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const fetchJson = async (url) => await fetchUrl(url, 'application/json', 'Resource');
|
|
108
|
+
const fetchHtml = async (url) => await fetchUrl(url, 'text/html,application/xhtml+xml', 'Article');
|
|
109
|
+
/**
|
|
110
|
+
* Clean HTML content by removing unwanted elements
|
|
111
|
+
*/
|
|
112
|
+
function cleanHtml($) {
|
|
113
|
+
// Remove unwanted elements
|
|
114
|
+
$(SELECTORS.REMOVE).remove();
|
|
115
|
+
// Fix relative URLs
|
|
116
|
+
$('a[href^="/"]').each((_, el) => {
|
|
117
|
+
const href = $(el).attr('href');
|
|
118
|
+
if (href !== undefined && href !== '') {
|
|
119
|
+
$(el).attr('href', `${DOCS_BASE_URL}${href}`);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
$('img[src^="/"]').each((_, el) => {
|
|
123
|
+
const src = $(el).attr('src');
|
|
124
|
+
if (src !== undefined && src !== '') {
|
|
125
|
+
$(el).attr('src', `${DOCS_BASE_URL}${src}`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Extract product and version from URL
|
|
131
|
+
* Supports both old docs.jamf.com and new learn.jamf.com URL structures
|
|
132
|
+
*/
|
|
133
|
+
function extractProductInfo(url) {
|
|
134
|
+
// learn.jamf.com structure: /en-US/bundle/{product}-documentation/page/{page}.html
|
|
135
|
+
const bundleMatch = /\/bundle\/([^/]+)-documentation\//.exec(url);
|
|
136
|
+
if (bundleMatch !== null) {
|
|
137
|
+
const product = Object.values(JAMF_PRODUCTS).find(p => p.bundleId.includes(bundleMatch[1] ?? ''));
|
|
138
|
+
return { product: product?.name, version: 'current' };
|
|
139
|
+
}
|
|
140
|
+
// Legacy docs.jamf.com structure: /{version}/{product}/...
|
|
141
|
+
const pathParts = new URL(url).pathname.split('/').filter(Boolean);
|
|
142
|
+
const versionMatch = pathParts[0]?.match(/^\d+\.\d+(\.\d+)?$/);
|
|
143
|
+
if (versionMatch !== null && versionMatch !== undefined) {
|
|
144
|
+
const product = Object.values(JAMF_PRODUCTS).find(p => p.urlPattern.includes(pathParts[1] ?? ''));
|
|
145
|
+
return { product: product?.name, version: pathParts[0] };
|
|
146
|
+
}
|
|
147
|
+
// Check for unversioned paths
|
|
148
|
+
const matchedProduct = Object.values(JAMF_PRODUCTS).find(p => url.includes(p.bundleId) || url.includes(p.id));
|
|
149
|
+
return { product: matchedProduct?.name, version: matchedProduct !== undefined ? 'current' : undefined };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if a search result matches a topic based on keywords
|
|
153
|
+
*/
|
|
154
|
+
function matchesTopic(result, topicId) {
|
|
155
|
+
const topic = JAMF_TOPICS[topicId];
|
|
156
|
+
const searchText = `${result.title} ${result.snippet}`.toLowerCase();
|
|
157
|
+
return topic.keywords.some(keyword => searchText.includes(keyword.toLowerCase()));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Search Jamf documentation using Zoomin Search API
|
|
161
|
+
*/
|
|
162
|
+
export async function searchDocumentation(params) {
|
|
163
|
+
const page = params.page ?? PAGINATION_CONFIG.DEFAULT_PAGE;
|
|
164
|
+
const pageSize = params.limit ?? CONTENT_LIMITS.DEFAULT_SEARCH_RESULTS;
|
|
165
|
+
const maxTokens = params.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
|
|
166
|
+
const cacheKey = `search:${JSON.stringify({ ...params, page: 1 })}`; // Cache without page for full results
|
|
167
|
+
// Check cache for full results
|
|
168
|
+
let allResults = await cache.get(cacheKey);
|
|
169
|
+
if (allResults === null) {
|
|
170
|
+
// Fetch more results to allow for filtering
|
|
171
|
+
const fetchLimit = CONTENT_LIMITS.MAX_SEARCH_RESULTS;
|
|
172
|
+
// Build API URL with query params
|
|
173
|
+
const apiUrl = new URL(`${DOCS_API_URL}/api/search`);
|
|
174
|
+
apiUrl.searchParams.set('q', params.query);
|
|
175
|
+
apiUrl.searchParams.set('rpp', fetchLimit.toString());
|
|
176
|
+
console.error(`[SEARCH] Query: "${params.query}", Product: ${params.product ?? 'all'}, Topic: ${params.topic ?? 'all'}, URL: ${apiUrl.toString()}`);
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetchJson(apiUrl.toString());
|
|
179
|
+
// Transform Zoomin results to SearchResult format with metadata
|
|
180
|
+
allResults = response.Results
|
|
181
|
+
.filter((wrapper) => wrapper.leading_result !== null && wrapper.leading_result !== undefined)
|
|
182
|
+
.map(wrapper => {
|
|
183
|
+
const result = wrapper.leading_result;
|
|
184
|
+
// Extract product from bundle_id
|
|
185
|
+
const bundleId = result.bundle_id;
|
|
186
|
+
const bundleMatch = /^(jamf-[a-z]+)-documentation/.exec(bundleId);
|
|
187
|
+
const bundleSlug = bundleMatch?.[1] ?? null;
|
|
188
|
+
const productEntry = bundleSlug !== null
|
|
189
|
+
? Object.entries(JAMF_PRODUCTS).find(([id]) => id === bundleSlug)
|
|
190
|
+
: null;
|
|
191
|
+
const searchResult = {
|
|
192
|
+
title: result.title !== '' ? result.title : 'Untitled',
|
|
193
|
+
url: transformToFrontendUrl(result.url !== '' ? result.url : ''),
|
|
194
|
+
snippet: stripHtml(result.snippet !== '' ? result.snippet : '').slice(0, CONTENT_LIMITS.MAX_SNIPPET_LENGTH),
|
|
195
|
+
product: productEntry !== null && productEntry !== undefined ? productEntry[1].name : (result.publication_title !== '' ? result.publication_title : 'Jamf'),
|
|
196
|
+
version: 'current'
|
|
197
|
+
};
|
|
198
|
+
if (result.score !== undefined) {
|
|
199
|
+
searchResult.relevance = result.score;
|
|
200
|
+
}
|
|
201
|
+
// Find matched topics
|
|
202
|
+
const matchedTopics = Object.keys(JAMF_TOPICS)
|
|
203
|
+
.filter(topicId => matchesTopic(searchResult, topicId));
|
|
204
|
+
return { result: searchResult, bundleSlug, matchedTopics };
|
|
205
|
+
});
|
|
206
|
+
// Cache full results
|
|
207
|
+
await cache.set(cacheKey, allResults);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error('[SEARCH] Error:', error);
|
|
211
|
+
if (error instanceof JamfDocsError) {
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
// Return empty results on error
|
|
215
|
+
return {
|
|
216
|
+
results: [],
|
|
217
|
+
pagination: {
|
|
218
|
+
page: 1,
|
|
219
|
+
pageSize,
|
|
220
|
+
totalPages: 0,
|
|
221
|
+
totalItems: 0,
|
|
222
|
+
hasNext: false,
|
|
223
|
+
hasPrev: false
|
|
224
|
+
},
|
|
225
|
+
tokenInfo: createTokenInfo('', maxTokens)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Apply filters
|
|
230
|
+
let filteredResults = allResults;
|
|
231
|
+
// Product filter
|
|
232
|
+
if (params.product !== undefined) {
|
|
233
|
+
filteredResults = filteredResults.filter(r => r.bundleSlug === params.product);
|
|
234
|
+
}
|
|
235
|
+
// Topic filter
|
|
236
|
+
if (params.topic !== undefined) {
|
|
237
|
+
const topicFilter = params.topic;
|
|
238
|
+
filteredResults = filteredResults.filter(r => r.matchedTopics.includes(topicFilter));
|
|
239
|
+
}
|
|
240
|
+
// Calculate pagination
|
|
241
|
+
const paginationInfo = calculatePagination(filteredResults.length, page, pageSize);
|
|
242
|
+
// Get paginated results
|
|
243
|
+
const paginatedResults = filteredResults
|
|
244
|
+
.slice(paginationInfo.startIndex, paginationInfo.endIndex)
|
|
245
|
+
.map(r => r.result);
|
|
246
|
+
// Calculate token info
|
|
247
|
+
const resultText = paginatedResults.map(r => `${r.title}\n${r.snippet}\n${r.url}`).join('\n\n');
|
|
248
|
+
const tokenCount = estimateTokens(resultText);
|
|
249
|
+
// Check if we need to truncate
|
|
250
|
+
let finalResults = paginatedResults;
|
|
251
|
+
let truncated = false;
|
|
252
|
+
if (tokenCount > maxTokens) {
|
|
253
|
+
// Truncate results to fit token limit
|
|
254
|
+
let runningTokens = 0;
|
|
255
|
+
finalResults = [];
|
|
256
|
+
for (const result of paginatedResults) {
|
|
257
|
+
const resultTokens = estimateTokens(`${result.title}\n${result.snippet}\n${result.url}`);
|
|
258
|
+
if (runningTokens + resultTokens > maxTokens) {
|
|
259
|
+
truncated = true;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
finalResults.push(result);
|
|
263
|
+
runningTokens += resultTokens;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
results: finalResults,
|
|
268
|
+
pagination: {
|
|
269
|
+
page: paginationInfo.page,
|
|
270
|
+
pageSize: paginationInfo.pageSize,
|
|
271
|
+
totalPages: paginationInfo.totalPages,
|
|
272
|
+
totalItems: filteredResults.length,
|
|
273
|
+
hasNext: paginationInfo.hasNext || truncated,
|
|
274
|
+
hasPrev: paginationInfo.hasPrev
|
|
275
|
+
},
|
|
276
|
+
tokenInfo: {
|
|
277
|
+
tokenCount: estimateTokens(finalResults.map(r => `${r.title}\n${r.snippet}\n${r.url}`).join('\n\n')),
|
|
278
|
+
truncated,
|
|
279
|
+
maxTokens
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Fetch and parse a documentation article
|
|
285
|
+
* Uses backend URL (learn-be.jamf.com) for pre-rendered content
|
|
286
|
+
*/
|
|
287
|
+
export async function fetchArticle(url, options = {}) {
|
|
288
|
+
const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
|
|
289
|
+
// Store original URL for display, use backend URL for fetching
|
|
290
|
+
const displayUrl = transformToFrontendUrl(url);
|
|
291
|
+
const articleFetchUrl = transformToBackendUrl(url);
|
|
292
|
+
const cacheKey = `article:${displayUrl}`;
|
|
293
|
+
// Check cache for raw article (without section/token processing)
|
|
294
|
+
let rawArticle = await cache.get(cacheKey);
|
|
295
|
+
if (rawArticle === null) {
|
|
296
|
+
// Fetch HTML from backend URL (pre-rendered content, not SPA shell)
|
|
297
|
+
const html = await fetchHtml(articleFetchUrl);
|
|
298
|
+
const $ = cheerio.load(html);
|
|
299
|
+
// Clean content
|
|
300
|
+
cleanHtml($);
|
|
301
|
+
// Extract content
|
|
302
|
+
const contentHtml = $(SELECTORS.CONTENT).html() ?? '';
|
|
303
|
+
const extractedTitle = $(SELECTORS.TITLE).first().text().trim();
|
|
304
|
+
const title = extractedTitle !== '' ? extractedTitle : 'Untitled';
|
|
305
|
+
// Convert to Markdown
|
|
306
|
+
const content = turndown.turndown(contentHtml);
|
|
307
|
+
// Extract breadcrumb
|
|
308
|
+
const breadcrumb = $(SELECTORS.BREADCRUMB)
|
|
309
|
+
.map((_, el) => $(el).text().trim())
|
|
310
|
+
.get()
|
|
311
|
+
.filter(Boolean);
|
|
312
|
+
// Extract related articles
|
|
313
|
+
const relatedArticles = options.includeRelated === true
|
|
314
|
+
? $(SELECTORS.RELATED).map((_, el) => ({
|
|
315
|
+
title: $(el).text().trim(),
|
|
316
|
+
url: $(el).attr('href') ?? ''
|
|
317
|
+
})).get().filter(r => r.title !== '' && r.url !== '')
|
|
318
|
+
: undefined;
|
|
319
|
+
// Extract product info from URL
|
|
320
|
+
const { product, version } = extractProductInfo(displayUrl);
|
|
321
|
+
rawArticle = {
|
|
322
|
+
title,
|
|
323
|
+
content,
|
|
324
|
+
url: displayUrl,
|
|
325
|
+
product,
|
|
326
|
+
version,
|
|
327
|
+
breadcrumb: breadcrumb.length > 0 ? breadcrumb : undefined,
|
|
328
|
+
relatedArticles: relatedArticles !== undefined && relatedArticles.length > 0 ? relatedArticles : undefined
|
|
329
|
+
};
|
|
330
|
+
// Cache raw article
|
|
331
|
+
await cache.set(cacheKey, rawArticle);
|
|
332
|
+
}
|
|
333
|
+
// At this point rawArticle is guaranteed to be non-null
|
|
334
|
+
const article = rawArticle;
|
|
335
|
+
// Extract all sections
|
|
336
|
+
const allSections = extractSections(article.content);
|
|
337
|
+
// Handle summaryOnly mode
|
|
338
|
+
if (options.summaryOnly === true) {
|
|
339
|
+
const summaryResult = extractSummary(article.content, article.title, maxTokens);
|
|
340
|
+
// Build summary content
|
|
341
|
+
let summaryContent = `## Summary\n\n${summaryResult.summary}\n\n`;
|
|
342
|
+
summaryContent += `## Article Outline (${summaryResult.outline.length} sections)\n\n`;
|
|
343
|
+
for (const section of summaryResult.outline) {
|
|
344
|
+
const indent = ' '.repeat(Math.max(0, section.level - 1));
|
|
345
|
+
summaryContent += `${indent}- ${section.title} (~${section.tokenCount} tokens)\n`;
|
|
346
|
+
}
|
|
347
|
+
summaryContent += `\n*Estimated read time: ${summaryResult.estimatedReadTime} min | Total: ${summaryResult.totalTokens.toLocaleString()} tokens*\n`;
|
|
348
|
+
return {
|
|
349
|
+
...article,
|
|
350
|
+
content: summaryContent,
|
|
351
|
+
tokenInfo: summaryResult.tokenInfo,
|
|
352
|
+
sections: allSections
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Handle section extraction if requested
|
|
356
|
+
let processedContent;
|
|
357
|
+
let tokenInfo;
|
|
358
|
+
if (options.section !== undefined && options.section !== '') {
|
|
359
|
+
// Extract specific section
|
|
360
|
+
const sectionResult = extractSection(article.content, options.section, maxTokens);
|
|
361
|
+
processedContent = sectionResult.content;
|
|
362
|
+
tokenInfo = sectionResult.tokenInfo;
|
|
363
|
+
if (sectionResult.section === null) {
|
|
364
|
+
// Section not found, return error info
|
|
365
|
+
processedContent = `*Section "${options.section}" not found.*\n\n**Available sections:**\n${allSections.map(s => `- ${s.title}`).join('\n')}`;
|
|
366
|
+
tokenInfo = createTokenInfo(processedContent, maxTokens);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
// Apply token limit with smart truncation
|
|
371
|
+
const truncateResult = truncateToTokenLimit(article.content, maxTokens);
|
|
372
|
+
processedContent = truncateResult.content;
|
|
373
|
+
tokenInfo = truncateResult.tokenInfo;
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
...article,
|
|
377
|
+
content: processedContent,
|
|
378
|
+
tokenInfo,
|
|
379
|
+
sections: allSections
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Discover the latest bundle version for a product via search API
|
|
384
|
+
*/
|
|
385
|
+
async function discoverLatestBundleId(product) {
|
|
386
|
+
const productInfo = JAMF_PRODUCTS[product];
|
|
387
|
+
const baseBundleId = productInfo.bundleId;
|
|
388
|
+
try {
|
|
389
|
+
// Search for any document from this product to discover the latest version
|
|
390
|
+
const apiUrl = `${DOCS_API_URL}/api/search?q=${encodeURIComponent(productInfo.name)}&rpp=10`;
|
|
391
|
+
const response = await fetchJson(apiUrl);
|
|
392
|
+
// Find a result with a matching bundle prefix
|
|
393
|
+
for (const wrapper of response.Results) {
|
|
394
|
+
if (wrapper.leading_result === null || wrapper.leading_result === undefined) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const bundleId = wrapper.leading_result.bundle_id;
|
|
398
|
+
if (bundleId.startsWith(baseBundleId)) {
|
|
399
|
+
return bundleId;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
console.error(`[TOC] Error discovering bundle version for ${product}:`, error);
|
|
405
|
+
}
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Parse TOC HTML from Zoomin backend
|
|
410
|
+
*/
|
|
411
|
+
function parseTocHtml(html) {
|
|
412
|
+
const $ = cheerio.load(html);
|
|
413
|
+
const toc = [];
|
|
414
|
+
// Parse the nested list structure
|
|
415
|
+
$('ul.list-links > li.toc').each((_, el) => {
|
|
416
|
+
const entry = parseTocEntry($, el);
|
|
417
|
+
if (entry !== null) {
|
|
418
|
+
toc.push(entry);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
return toc;
|
|
422
|
+
}
|
|
423
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
424
|
+
function parseTocEntry($, el) {
|
|
425
|
+
const $el = $(el);
|
|
426
|
+
const $link = $el.children('.inner').children('a').first();
|
|
427
|
+
const title = $link.text().trim();
|
|
428
|
+
let url = $link.attr('href') ?? '';
|
|
429
|
+
if (title === '' || url === '') {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
// Transform to frontend URL for display
|
|
433
|
+
url = transformToFrontendUrl(url);
|
|
434
|
+
const entry = { title, url };
|
|
435
|
+
// Check for children
|
|
436
|
+
const $children = $el.children('ul.list-links');
|
|
437
|
+
if ($children.length > 0) {
|
|
438
|
+
const children = [];
|
|
439
|
+
$children.children('li.toc').each((_, childEl) => {
|
|
440
|
+
const childEntry = parseTocEntry($, childEl);
|
|
441
|
+
if (childEntry !== null) {
|
|
442
|
+
children.push(childEntry);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
if (children.length > 0) {
|
|
446
|
+
entry.children = children;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return entry;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Convert TOC entry to string for token counting
|
|
453
|
+
*/
|
|
454
|
+
function tocEntryToString(entry, depth = 0) {
|
|
455
|
+
const indent = ' '.repeat(depth);
|
|
456
|
+
const childrenStr = entry.children?.map(c => tocEntryToString(c, depth + 1)).join('') ?? '';
|
|
457
|
+
return `${indent}- ${entry.title}\n${childrenStr}`;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Count total TOC entries including children
|
|
461
|
+
*/
|
|
462
|
+
function countTocEntries(entries) {
|
|
463
|
+
return entries.reduce((count, entry) => count + 1 + (entry.children !== undefined ? countTocEntries(entry.children) : 0), 0);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Fetch table of contents for a product
|
|
467
|
+
* Uses backend TOC endpoint (learn-be.jamf.com/bundle/{bundleId}/toc)
|
|
468
|
+
*/
|
|
469
|
+
export async function fetchTableOfContents(product, version = 'current', options = {}) {
|
|
470
|
+
const page = options.page ?? PAGINATION_CONFIG.DEFAULT_PAGE;
|
|
471
|
+
const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
|
|
472
|
+
const cacheKey = `toc:${product}:${version}`;
|
|
473
|
+
// Check cache
|
|
474
|
+
let allToc = await cache.get(cacheKey);
|
|
475
|
+
if (allToc === null) {
|
|
476
|
+
// Get bundle ID for the specified version (or latest if current)
|
|
477
|
+
let bundleId = await getBundleIdForVersion(product, version);
|
|
478
|
+
// Fallback to discovery if metadata service fails
|
|
479
|
+
bundleId ??= await discoverLatestBundleId(product);
|
|
480
|
+
if (bundleId === null || bundleId === '') {
|
|
481
|
+
throw new JamfDocsError(`Could not find bundle for ${product} version ${version}`, JamfDocsErrorCode.NOT_FOUND);
|
|
482
|
+
}
|
|
483
|
+
// Fetch TOC from backend
|
|
484
|
+
const tocUrl = `${DOCS_API_URL}/bundle/${bundleId}/toc`;
|
|
485
|
+
console.error(`[TOC] Fetching TOC from: ${tocUrl}`);
|
|
486
|
+
const tocJson = await fetchJson(tocUrl);
|
|
487
|
+
// Parse TOC
|
|
488
|
+
allToc = [];
|
|
489
|
+
for (const [, html] of Object.entries(tocJson)) {
|
|
490
|
+
if (typeof html === 'string' && html.includes('<ul')) {
|
|
491
|
+
const entries = parseTocHtml(html);
|
|
492
|
+
allToc.push(...entries);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Cache result
|
|
496
|
+
await cache.set(cacheKey, allToc);
|
|
497
|
+
}
|
|
498
|
+
// Count total entries
|
|
499
|
+
const totalItems = countTocEntries(allToc);
|
|
500
|
+
// For TOC, we paginate at the top-level entries
|
|
501
|
+
const topLevelCount = allToc.length;
|
|
502
|
+
const pageSize = PAGINATION_CONFIG.DEFAULT_PAGE_SIZE;
|
|
503
|
+
const paginationInfo = calculatePagination(topLevelCount, page, pageSize);
|
|
504
|
+
// Get paginated top-level entries (with their children)
|
|
505
|
+
const paginatedToc = allToc.slice(paginationInfo.startIndex, paginationInfo.endIndex);
|
|
506
|
+
// Calculate tokens for paginated TOC
|
|
507
|
+
const tocText = paginatedToc.map(e => tocEntryToString(e)).join('');
|
|
508
|
+
let tokenCount = estimateTokens(tocText);
|
|
509
|
+
let truncated = false;
|
|
510
|
+
let finalToc = paginatedToc;
|
|
511
|
+
// Truncate if over token limit
|
|
512
|
+
if (tokenCount > maxTokens) {
|
|
513
|
+
truncated = true;
|
|
514
|
+
finalToc = [];
|
|
515
|
+
let runningTokens = 0;
|
|
516
|
+
for (const entry of paginatedToc) {
|
|
517
|
+
const entryText = tocEntryToString(entry);
|
|
518
|
+
const entryTokens = estimateTokens(entryText);
|
|
519
|
+
if (runningTokens + entryTokens > maxTokens) {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
finalToc.push(entry);
|
|
523
|
+
runningTokens += entryTokens;
|
|
524
|
+
}
|
|
525
|
+
tokenCount = runningTokens;
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
toc: finalToc,
|
|
529
|
+
pagination: {
|
|
530
|
+
page: paginationInfo.page,
|
|
531
|
+
pageSize: paginationInfo.pageSize,
|
|
532
|
+
totalPages: paginationInfo.totalPages,
|
|
533
|
+
totalItems,
|
|
534
|
+
hasNext: paginationInfo.hasNext || truncated,
|
|
535
|
+
hasPrev: paginationInfo.hasPrev
|
|
536
|
+
},
|
|
537
|
+
tokenInfo: {
|
|
538
|
+
tokenCount,
|
|
539
|
+
truncated,
|
|
540
|
+
maxTokens
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=scraper.js.map
|