@goxtechnologies/connectwise-psa-mcp 1.3.0 → 1.4.2
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/data/reports.json +29 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/operations/analytics-extended.d.ts.map +1 -1
- package/dist/operations/analytics-extended.js +15 -4
- package/dist/operations/analytics-extended.js.map +1 -1
- package/dist/operations/analytics-msp-schedule.js +12 -2
- package/dist/operations/analytics-msp-schedule.js.map +1 -1
- package/dist/operations/analytics-msp-time-entry.d.ts +68 -0
- package/dist/operations/analytics-msp-time-entry.d.ts.map +1 -0
- package/dist/operations/analytics-msp-time-entry.js +235 -0
- package/dist/operations/analytics-msp-time-entry.js.map +1 -0
- package/dist/operations/analytics-msp-time.js +27 -8
- package/dist/operations/analytics-msp-time.js.map +1 -1
- package/dist/operations/analytics.d.ts.map +1 -1
- package/dist/operations/analytics.js +2 -0
- package/dist/operations/analytics.js.map +1 -1
- package/dist/operations/registry.d.ts.map +1 -1
- package/dist/operations/registry.js +1 -0
- package/dist/operations/registry.js.map +1 -1
- package/dist/scrapers/index.d.ts +4 -0
- package/dist/scrapers/index.d.ts.map +1 -0
- package/dist/scrapers/index.js +10 -0
- package/dist/scrapers/index.js.map +1 -0
- package/dist/scrapers/reports.d.ts +3 -0
- package/dist/scrapers/reports.d.ts.map +1 -0
- package/dist/scrapers/reports.js +443 -0
- package/dist/scrapers/reports.js.map +1 -0
- package/dist/services/load-env.d.ts.map +1 -1
- package/dist/services/load-env.js +11 -1
- package/dist/services/load-env.js.map +1 -1
- package/dist/services/report-cache.d.ts +16 -0
- package/dist/services/report-cache.d.ts.map +1 -0
- package/dist/services/report-cache.js +230 -0
- package/dist/services/report-cache.js.map +1 -0
- package/dist/services/report-config.d.ts +28 -0
- package/dist/services/report-config.d.ts.map +1 -0
- package/dist/services/report-config.js +127 -0
- package/dist/services/report-config.js.map +1 -0
- package/dist/services/totp.d.ts +27 -0
- package/dist/services/totp.d.ts.map +1 -0
- package/dist/services/totp.js +76 -0
- package/dist/services/totp.js.map +1 -0
- package/dist/services/web-scraper.d.ts +76 -0
- package/dist/services/web-scraper.d.ts.map +1 -0
- package/dist/services/web-scraper.js +435 -0
- package/dist/services/web-scraper.js.map +1 -0
- package/dist/tools/reports.d.ts +9 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +388 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/validation.js +2 -2
- package/dist/tools/validation.js.map +1 -1
- package/dist/types/reports.d.ts +87 -0
- package/dist/types/reports.d.ts.map +1 -0
- package/dist/types/reports.js +3 -0
- package/dist/types/reports.js.map +1 -0
- package/package.json +18 -4
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
// ConnectWise PSA MCP Server — Report Extraction Handlers
|
|
2
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
+
import { getWebScraper } from '../services/web-scraper.js';
|
|
4
|
+
import { resolveTemplateVars, getTemplateVars } from '../services/report-config.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Handler registry
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
export const reportScrapers = {};
|
|
9
|
+
function reg(name, handler) {
|
|
10
|
+
reportScrapers[name] = handler;
|
|
11
|
+
}
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Navigation
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* Navigate to a report using either 'url' or 'menu' strategy, then fill
|
|
17
|
+
* parameters and click the run button if one is discoverable.
|
|
18
|
+
*/
|
|
19
|
+
async function navigateToReport(page, reportDef, params) {
|
|
20
|
+
const nav = reportDef.navigation;
|
|
21
|
+
if (nav.strategy === 'url') {
|
|
22
|
+
const template = nav.url ?? reportDef.url ?? '';
|
|
23
|
+
const vars = {
|
|
24
|
+
...getTemplateVars(),
|
|
25
|
+
...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v ?? '')])),
|
|
26
|
+
};
|
|
27
|
+
const resolvedUrl = resolveTemplateVars(template, vars);
|
|
28
|
+
await page.goto(resolvedUrl, { waitUntil: 'domcontentloaded' });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// 'menu' strategy: navigate to CW base, then click through path items
|
|
32
|
+
const vars = getTemplateVars();
|
|
33
|
+
const cwBase = vars['cw_base'] ?? '';
|
|
34
|
+
await page.goto(cwBase, { waitUntil: 'domcontentloaded' });
|
|
35
|
+
if (nav.path && nav.path.length > 0) {
|
|
36
|
+
for (const menuItem of nav.path) {
|
|
37
|
+
try {
|
|
38
|
+
// Try text match first, then aria-label
|
|
39
|
+
const el = (await page.$(`text="${menuItem}"`)) ??
|
|
40
|
+
(await page.$(`[aria-label="${menuItem}"]`)) ??
|
|
41
|
+
(await page.$(`[title="${menuItem}"]`));
|
|
42
|
+
if (el) {
|
|
43
|
+
await el.click();
|
|
44
|
+
await page.waitForTimeout(600);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Best-effort — continue through remaining path items
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Fill in report parameters using selectors defined in the report definition
|
|
54
|
+
for (const param of reportDef.parameters) {
|
|
55
|
+
const value = params[param.name] ?? param.default;
|
|
56
|
+
if (value == null)
|
|
57
|
+
continue;
|
|
58
|
+
try {
|
|
59
|
+
const el = await page.$(param.selector);
|
|
60
|
+
if (!el)
|
|
61
|
+
continue;
|
|
62
|
+
const tag = await el.evaluate((n) => n.tagName.toLowerCase());
|
|
63
|
+
if (tag === 'select') {
|
|
64
|
+
await page.selectOption(param.selector, String(value));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
await page.fill(param.selector, String(value));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Best-effort — skip parameters that can't be filled
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Click run/view/generate button if present
|
|
75
|
+
const runSelectors = [
|
|
76
|
+
'button:has-text("Run")',
|
|
77
|
+
'button:has-text("View Report")',
|
|
78
|
+
'button:has-text("Generate")',
|
|
79
|
+
'input[type="submit"]',
|
|
80
|
+
'button[type="submit"]',
|
|
81
|
+
];
|
|
82
|
+
for (const sel of runSelectors) {
|
|
83
|
+
try {
|
|
84
|
+
const btn = await page.$(sel);
|
|
85
|
+
if (btn) {
|
|
86
|
+
await btn.click();
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Continue
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Frame detection
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
/**
|
|
99
|
+
* Check page frames for report/ssrs/viewer URLs.
|
|
100
|
+
* Returns the first matching frame, or null if none found.
|
|
101
|
+
*/
|
|
102
|
+
function detectReportFrame(page) {
|
|
103
|
+
for (const frame of page.frames()) {
|
|
104
|
+
const url = frame.url().toLowerCase();
|
|
105
|
+
if (url.includes('report') || url.includes('ssrs') || url.includes('viewer')) {
|
|
106
|
+
return frame;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// XHR interception
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Trigger an action while capturing network responses whose URL contains
|
|
116
|
+
* `pattern`, then attempt to parse the first matching JSON response as
|
|
117
|
+
* ReportData. Returns null if no usable response is captured within the
|
|
118
|
+
* timeout.
|
|
119
|
+
*/
|
|
120
|
+
async function tryXHRExtraction(page, pattern, timeout) {
|
|
121
|
+
const scraper = getWebScraper();
|
|
122
|
+
let captured = [];
|
|
123
|
+
try {
|
|
124
|
+
captured = await scraper.captureResponses(page, async () => {
|
|
125
|
+
// The page has already been navigated; just wait for any triggered XHR
|
|
126
|
+
await page.waitForTimeout(timeout);
|
|
127
|
+
}, pattern, timeout);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
for (const response of captured) {
|
|
133
|
+
try {
|
|
134
|
+
const text = await response.text();
|
|
135
|
+
const json = JSON.parse(text);
|
|
136
|
+
// Accept arrays of objects
|
|
137
|
+
if (Array.isArray(json) && json.length > 0 && typeof json[0] === 'object') {
|
|
138
|
+
return arrayToReportData(json);
|
|
139
|
+
}
|
|
140
|
+
// Accept {rows: [...]} or {data: [...]} wrappers
|
|
141
|
+
if (json && typeof json === 'object') {
|
|
142
|
+
const obj = json;
|
|
143
|
+
const inner = obj['rows'] ?? obj['data'] ?? obj['value'] ?? obj['results'];
|
|
144
|
+
if (Array.isArray(inner) && inner.length > 0 && typeof inner[0] === 'object') {
|
|
145
|
+
return arrayToReportData(inner);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Not JSON or not parseable — try next
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Export button fallback
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
/**
|
|
159
|
+
* Find and click an export button, then wait for a file download, read it,
|
|
160
|
+
* and parse as CSV. Returns null if the button isn't found or the download
|
|
161
|
+
* times out.
|
|
162
|
+
*/
|
|
163
|
+
async function tryExportExtraction(page, frame, exportSelector) {
|
|
164
|
+
const ctx = frame ?? page;
|
|
165
|
+
let exportBtn = null;
|
|
166
|
+
try {
|
|
167
|
+
exportBtn = await ctx.$(exportSelector);
|
|
168
|
+
if (!exportBtn) {
|
|
169
|
+
// Try broader fallback selectors (3s)
|
|
170
|
+
const fallbacks = [
|
|
171
|
+
'button:has-text("Export")',
|
|
172
|
+
'a:has-text("Export")',
|
|
173
|
+
'[title*="Export"]',
|
|
174
|
+
'[aria-label*="Export"]',
|
|
175
|
+
];
|
|
176
|
+
for (const sel of fallbacks) {
|
|
177
|
+
try {
|
|
178
|
+
exportBtn = await ctx.$(sel);
|
|
179
|
+
if (exportBtn)
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// continue
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// selector error
|
|
190
|
+
}
|
|
191
|
+
if (!exportBtn)
|
|
192
|
+
return null;
|
|
193
|
+
try {
|
|
194
|
+
const [download] = await Promise.all([
|
|
195
|
+
page.waitForEvent('download', { timeout: 15_000 }),
|
|
196
|
+
exportBtn.click(),
|
|
197
|
+
]);
|
|
198
|
+
const downloadPath = await download.path();
|
|
199
|
+
if (!downloadPath || !existsSync(downloadPath))
|
|
200
|
+
return null;
|
|
201
|
+
const csvContent = readFileSync(downloadPath, 'utf-8');
|
|
202
|
+
return parseCSV(csvContent);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// DOM table extraction
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
/**
|
|
212
|
+
* Extract a table from the DOM (via page.evaluate). Falls back to looking for
|
|
213
|
+
* any <table> if tableSelector doesn't match. Always returns a ReportData —
|
|
214
|
+
* may have empty columns/rows if no table is found.
|
|
215
|
+
*/
|
|
216
|
+
async function extractFromDOM(ctx, tableSelector) {
|
|
217
|
+
const result = await ctx.evaluate(({ selector }) => {
|
|
218
|
+
// Find the table element
|
|
219
|
+
let table = document.querySelector(selector);
|
|
220
|
+
if (!table) {
|
|
221
|
+
table = document.querySelector('table');
|
|
222
|
+
}
|
|
223
|
+
if (!table)
|
|
224
|
+
return { columns: [], rows: [] };
|
|
225
|
+
const columns = [];
|
|
226
|
+
const rows = [];
|
|
227
|
+
// Extract headers
|
|
228
|
+
const headerCells = table.querySelectorAll('thead th, thead td, tr:first-child th');
|
|
229
|
+
if (headerCells.length > 0) {
|
|
230
|
+
headerCells.forEach((cell) => columns.push(cell.textContent?.trim() ?? ''));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
// Fallback: use first row as header
|
|
234
|
+
const firstRow = table.querySelector('tr');
|
|
235
|
+
if (firstRow) {
|
|
236
|
+
firstRow.querySelectorAll('td, th').forEach((cell) => columns.push(cell.textContent?.trim() ?? ''));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Extract data rows (skip header row if it was in tbody)
|
|
240
|
+
const bodyRows = table.querySelectorAll('tbody tr');
|
|
241
|
+
const rowSource = bodyRows.length > 0 ? bodyRows : table.querySelectorAll('tr');
|
|
242
|
+
rowSource.forEach((tr, rowIndex) => {
|
|
243
|
+
// Skip first row if it was used as the header above (no thead)
|
|
244
|
+
if (bodyRows.length === 0 && rowIndex === 0)
|
|
245
|
+
return;
|
|
246
|
+
const cells = [];
|
|
247
|
+
tr.querySelectorAll('td, th').forEach((cell) => cells.push(cell.textContent?.trim() ?? ''));
|
|
248
|
+
// Skip empty rows
|
|
249
|
+
if (cells.some((c) => c !== '')) {
|
|
250
|
+
rows.push(cells);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
return { columns, rows };
|
|
254
|
+
}, { selector: tableSelector });
|
|
255
|
+
// Normalize values: try to parse numbers, keep everything else as string
|
|
256
|
+
const normalizedRows = result.rows.map((cells) => {
|
|
257
|
+
const obj = {};
|
|
258
|
+
result.columns.forEach((col, idx) => {
|
|
259
|
+
const raw = cells[idx] ?? '';
|
|
260
|
+
if (raw === '' || raw === '-' || raw === 'N/A') {
|
|
261
|
+
obj[col] = null;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const num = Number(raw.replace(/[$,%\s]/g, ''));
|
|
265
|
+
obj[col] = Number.isFinite(num) && raw.replace(/[$,%\s]/g, '') !== '' ? num : raw;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
return obj;
|
|
269
|
+
});
|
|
270
|
+
return { columns: result.columns, rows: normalizedRows };
|
|
271
|
+
}
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// CSV parsing
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
/**
|
|
276
|
+
* Parse CSV content (with optional quoted fields) into ReportData.
|
|
277
|
+
* First row is treated as headers.
|
|
278
|
+
*/
|
|
279
|
+
function parseCSV(content) {
|
|
280
|
+
const lines = content.split(/\r?\n/).filter((l) => l.trim() !== '');
|
|
281
|
+
if (lines.length === 0)
|
|
282
|
+
return { columns: [], rows: [] };
|
|
283
|
+
const columns = parseCSVLine(lines[0]);
|
|
284
|
+
const rows = [];
|
|
285
|
+
for (let i = 1; i < lines.length; i++) {
|
|
286
|
+
const cells = parseCSVLine(lines[i]);
|
|
287
|
+
const obj = {};
|
|
288
|
+
columns.forEach((col, idx) => {
|
|
289
|
+
const raw = cells[idx] ?? '';
|
|
290
|
+
if (raw === '' || raw === 'N/A' || raw === '-') {
|
|
291
|
+
obj[col] = null;
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
const num = Number(raw.replace(/[$,%\s]/g, ''));
|
|
295
|
+
obj[col] = Number.isFinite(num) && raw.replace(/[$,%\s]/g, '') !== '' ? num : raw;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
rows.push(obj);
|
|
299
|
+
}
|
|
300
|
+
return { columns, rows };
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Parse a single CSV line, handling double-quoted fields with escaped quotes
|
|
304
|
+
* (RFC 4180 style: "" inside quotes means a literal ").
|
|
305
|
+
*/
|
|
306
|
+
function parseCSVLine(line) {
|
|
307
|
+
const fields = [];
|
|
308
|
+
let current = '';
|
|
309
|
+
let inQuotes = false;
|
|
310
|
+
let i = 0;
|
|
311
|
+
while (i < line.length) {
|
|
312
|
+
const ch = line[i];
|
|
313
|
+
if (inQuotes) {
|
|
314
|
+
if (ch === '"') {
|
|
315
|
+
// Peek ahead for escaped quote
|
|
316
|
+
if (i + 1 < line.length && line[i + 1] === '"') {
|
|
317
|
+
current += '"';
|
|
318
|
+
i += 2;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
inQuotes = false;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
current += ch;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
if (ch === '"') {
|
|
329
|
+
inQuotes = true;
|
|
330
|
+
}
|
|
331
|
+
else if (ch === ',') {
|
|
332
|
+
fields.push(current);
|
|
333
|
+
current = '';
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
current += ch;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
i++;
|
|
340
|
+
}
|
|
341
|
+
fields.push(current);
|
|
342
|
+
return fields;
|
|
343
|
+
}
|
|
344
|
+
// ---------------------------------------------------------------------------
|
|
345
|
+
// Array → ReportData conversion
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
/**
|
|
348
|
+
* Convert an array of plain objects (e.g., from an XHR JSON response) into
|
|
349
|
+
* the normalized {columns, rows} shape. Infers columns from the union of all
|
|
350
|
+
* object keys. Normalizes values to string | number | null.
|
|
351
|
+
*/
|
|
352
|
+
function arrayToReportData(arr) {
|
|
353
|
+
if (arr.length === 0)
|
|
354
|
+
return { columns: [], rows: [] };
|
|
355
|
+
// Derive column list from union of all keys (preserving first-seen order)
|
|
356
|
+
const columnSet = new Set();
|
|
357
|
+
for (const item of arr) {
|
|
358
|
+
for (const key of Object.keys(item)) {
|
|
359
|
+
columnSet.add(key);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const columns = Array.from(columnSet);
|
|
363
|
+
const rows = arr.map((item) => {
|
|
364
|
+
const obj = {};
|
|
365
|
+
for (const col of columns) {
|
|
366
|
+
const raw = item[col];
|
|
367
|
+
if (raw == null) {
|
|
368
|
+
obj[col] = null;
|
|
369
|
+
}
|
|
370
|
+
else if (typeof raw === 'number') {
|
|
371
|
+
obj[col] = raw;
|
|
372
|
+
}
|
|
373
|
+
else if (typeof raw === 'boolean') {
|
|
374
|
+
obj[col] = String(raw);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
const str = String(raw);
|
|
378
|
+
const num = Number(str.replace(/[$,%\s]/g, ''));
|
|
379
|
+
obj[col] =
|
|
380
|
+
Number.isFinite(num) && str.replace(/[$,%\s]/g, '') !== '' ? num : str;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return obj;
|
|
384
|
+
});
|
|
385
|
+
return { columns, rows };
|
|
386
|
+
}
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
// Main extraction pipeline
|
|
389
|
+
// ---------------------------------------------------------------------------
|
|
390
|
+
/**
|
|
391
|
+
* Generic extraction pipeline:
|
|
392
|
+
* 1. Acquire a page from the scraper singleton
|
|
393
|
+
* 2. Navigate to the report (via url or menu)
|
|
394
|
+
* 3. Wait for the `waitFor` selector (30s)
|
|
395
|
+
* 4. Try XHR interception (if strategy is 'xhr_first')
|
|
396
|
+
* 5. Try export button fallback
|
|
397
|
+
* 6. Fall back to DOM table extraction
|
|
398
|
+
*/
|
|
399
|
+
async function genericExtract(params, reportDef) {
|
|
400
|
+
const scraper = getWebScraper();
|
|
401
|
+
if (!scraper.isInitialized()) {
|
|
402
|
+
await scraper.init();
|
|
403
|
+
}
|
|
404
|
+
const page = await scraper.acquirePage();
|
|
405
|
+
try {
|
|
406
|
+
await navigateToReport(page, reportDef, params);
|
|
407
|
+
// Wait for the expected element to confirm the report has loaded
|
|
408
|
+
try {
|
|
409
|
+
await page.waitForSelector(reportDef.extraction.waitFor, { timeout: 30_000 });
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
// The selector didn't appear — proceed anyway (best-effort)
|
|
413
|
+
}
|
|
414
|
+
// Detect report iframe (SSRS / embedded viewers)
|
|
415
|
+
const frame = detectReportFrame(page);
|
|
416
|
+
// Strategy: XHR first
|
|
417
|
+
if (reportDef.extraction.strategy === 'xhr_first' && reportDef.extraction.xhrPattern) {
|
|
418
|
+
const xhrData = await tryXHRExtraction(page, reportDef.extraction.xhrPattern, 15_000);
|
|
419
|
+
if (xhrData && xhrData.columns.length > 0) {
|
|
420
|
+
return xhrData;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Export button fallback
|
|
424
|
+
const exportSelector = reportDef.extraction.exportSelector ?? '[title*="Export"], button:has-text("Export")';
|
|
425
|
+
const exportData = await tryExportExtraction(page, frame, exportSelector);
|
|
426
|
+
if (exportData && exportData.columns.length > 0) {
|
|
427
|
+
return exportData;
|
|
428
|
+
}
|
|
429
|
+
// DOM table extraction — always returns something
|
|
430
|
+
const tableSelector = reportDef.extraction.tableSelector ?? 'table';
|
|
431
|
+
const domCtx = frame ?? page;
|
|
432
|
+
return await extractFromDOM(domCtx, tableSelector);
|
|
433
|
+
}
|
|
434
|
+
finally {
|
|
435
|
+
scraper.releasePage(page);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
// Handler registrations
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
reg('__generic__', genericExtract);
|
|
442
|
+
reg('time_accrual', genericExtract);
|
|
443
|
+
//# sourceMappingURL=reports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reports.js","sourceRoot":"","sources":["../../src/scrapers/reports.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-env.d.ts","sourceRoot":"","sources":["../../src/services/load-env.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,yCAAyC;AACzC,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"load-env.d.ts","sourceRoot":"","sources":["../../src/services/load-env.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,yCAAyC;AACzC,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAqD9B;AAED,iEAAiE;AACjE,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Reads KEY=VALUE pairs from the plugin root's .env file and populates
|
|
4
4
|
* process.env as a fallback — existing environment variables take precedence.
|
|
5
5
|
*/
|
|
6
|
-
import { readFileSync, existsSync } from 'fs';
|
|
6
|
+
import { readFileSync, existsSync, statSync } from 'fs';
|
|
7
7
|
import { dirname, join, resolve } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -35,6 +35,16 @@ export function loadEnv() {
|
|
|
35
35
|
catch {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
+
// Warn if .env file is world-readable
|
|
39
|
+
try {
|
|
40
|
+
const stats = statSync(envPath);
|
|
41
|
+
const mode = stats.mode & 0o777;
|
|
42
|
+
if (mode & 0o044) {
|
|
43
|
+
console.error(`[connectwise-psa] WARNING: ${envPath} is readable by other users (mode: ${mode.toString(8)}). ` +
|
|
44
|
+
'This file contains sensitive credentials. Run: chmod 600 ' + envPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch { /* ignore on platforms without Unix permissions */ }
|
|
38
48
|
for (const line of content.split('\n')) {
|
|
39
49
|
const trimmed = line.trim();
|
|
40
50
|
if (!trimmed || trimmed.startsWith('#'))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-env.js","sourceRoot":"","sources":["../../src/services/load-env.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"load-env.js","sourceRoot":"","sources":["../../src/services/load-env.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReportData, CachedReport, HistoryEntry, CalibrationEntry } from '../types/reports.js';
|
|
2
|
+
export declare class ReportCache {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(dbPath?: string);
|
|
5
|
+
private initialize;
|
|
6
|
+
hashParams(params: Record<string, unknown>): string;
|
|
7
|
+
get(reportId: string, paramsHash: string, ttlMinutes: number): CachedReport | null;
|
|
8
|
+
set(reportId: string, params: Record<string, unknown>, data: ReportData, source: 'xhr' | 'export' | 'dom'): void;
|
|
9
|
+
invalidate(reportId?: string): void;
|
|
10
|
+
getHistory(reportId: string, limit?: number): HistoryEntry[];
|
|
11
|
+
purgeHistory(retentionDays: number): number;
|
|
12
|
+
logCalibration(reportId: string, params: Record<string, unknown>, webValue: string, apiValue: string, matchType: 'scalar' | 'row_count' | 'full_compare', drift?: number): void;
|
|
13
|
+
getCalibrationLog(reportId: string, limit?: number): CalibrationEntry[];
|
|
14
|
+
close(): void;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=report-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-cache.d.ts","sourceRoot":"","sources":["../../src/services/report-cache.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAyCpG,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,CAAC,EAAE,MAAM;IAU3B,OAAO,CAAC,UAAU;IAqDlB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IAUnD,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IA0BlF,GAAG,CACD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,GAC/B,IAAI;IAiCP,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAYnC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,YAAY,EAAE;IAsBxD,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;IAY3C,cAAc,CACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,QAAQ,GAAG,WAAW,GAAG,cAAc,EAClD,KAAK,CAAC,EAAE,MAAM,GACb,IAAI;IAkBP,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,gBAAgB,EAAE;IAwBnE,KAAK,IAAI,IAAI;CAGd"}
|