@xyteai/cli 0.1.0 → 0.2.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 +27 -5
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +52 -44
- package/dist/cli/index.js.map +1 -1
- package/dist/client/create-client.js +14 -14
- package/dist/client/create-client.js.map +1 -1
- package/dist/config/readiness.d.ts +2 -2
- package/dist/config/readiness.d.ts.map +1 -1
- package/dist/config/readiness.js +1 -1
- package/dist/config/readiness.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/namespaces/device.d.ts +1 -0
- package/dist/namespaces/device.d.ts.map +1 -1
- package/dist/namespaces/device.js +1 -0
- package/dist/namespaces/device.js.map +1 -1
- package/dist/namespaces/organization.d.ts +1 -0
- package/dist/namespaces/organization.d.ts.map +1 -1
- package/dist/namespaces/organization.js +1 -0
- package/dist/namespaces/organization.js.map +1 -1
- package/dist/secure/{keychain.d.ts → secret-store.d.ts} +16 -4
- package/dist/secure/secret-store.d.ts.map +1 -0
- package/dist/secure/secret-store.js +140 -0
- package/dist/secure/secret-store.js.map +1 -0
- package/dist/spec/public-endpoints.json +37 -0
- package/dist/tui/app.d.ts +2 -2
- package/dist/tui/app.d.ts.map +1 -1
- package/dist/tui/app.js +5 -5
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/headless-renderer.d.ts +2 -2
- package/dist/tui/headless-renderer.d.ts.map +1 -1
- package/dist/tui/headless-renderer.js +4 -4
- package/dist/tui/headless-renderer.js.map +1 -1
- package/dist/tui/key-wizard.d.ts +1 -1
- package/dist/tui/key-wizard.d.ts.map +1 -1
- package/dist/tui/key-wizard.js +2 -2
- package/dist/tui/key-wizard.js.map +1 -1
- package/dist/tui/screens/config.js +6 -6
- package/dist/tui/screens/config.js.map +1 -1
- package/dist/tui/types.d.ts +2 -2
- package/dist/tui/types.d.ts.map +1 -1
- package/dist/types/client.d.ts +2 -2
- package/dist/types/client.d.ts.map +1 -1
- package/dist/workflows/fleet-insights.d.ts +7 -8
- package/dist/workflows/fleet-insights.d.ts.map +1 -1
- package/dist/workflows/fleet-insights.js +10 -492
- package/dist/workflows/fleet-insights.js.map +1 -1
- package/dist/workflows/report/font-asset.d.ts +8 -0
- package/dist/workflows/report/font-asset.d.ts.map +1 -0
- package/dist/workflows/report/font-asset.js +1462 -0
- package/dist/workflows/report/font-asset.js.map +1 -0
- package/dist/workflows/report/logo-asset.d.ts +7 -0
- package/dist/workflows/report/logo-asset.d.ts.map +1 -0
- package/dist/workflows/report/logo-asset.js +707 -0
- package/dist/workflows/report/logo-asset.js.map +1 -0
- package/dist/workflows/report/pdf-layout.d.ts +59 -0
- package/dist/workflows/report/pdf-layout.d.ts.map +1 -0
- package/dist/workflows/report/pdf-layout.js +388 -0
- package/dist/workflows/report/pdf-layout.js.map +1 -0
- package/dist/workflows/report/pdf-render.d.ts +3 -0
- package/dist/workflows/report/pdf-render.d.ts.map +1 -0
- package/dist/workflows/report/pdf-render.js +221 -0
- package/dist/workflows/report/pdf-render.js.map +1 -0
- package/dist/workflows/report/pdf-table.d.ts +33 -0
- package/dist/workflows/report/pdf-table.d.ts.map +1 -0
- package/dist/workflows/report/pdf-table.js +213 -0
- package/dist/workflows/report/pdf-table.js.map +1 -0
- package/dist/workflows/report/theme.d.ts +53 -0
- package/dist/workflows/report/theme.d.ts.map +1 -0
- package/dist/workflows/report/theme.js +95 -0
- package/dist/workflows/report/theme.js.map +1 -0
- package/dist/workflows/report/time-format.d.ts +5 -0
- package/dist/workflows/report/time-format.d.ts.map +1 -0
- package/dist/workflows/report/time-format.js +112 -0
- package/dist/workflows/report/time-format.js.map +1 -0
- package/docs/schemas/inspect-deep-dive.v1.schema.json +3 -0
- package/package.json +5 -1
- package/skills/xyte-cli/SKILL.md +7 -2
- package/skills/xyte-cli/agents/openai.yaml +1 -1
- package/skills/xyte-cli/references/endpoints.md +23 -0
- package/skills/xyte-cli/scripts/check_headless.sh +1 -1
- package/dist/secure/keychain.d.ts.map +0 -1
- package/dist/secure/keychain.js +0 -170
- package/dist/secure/keychain.js.map +0 -1
package/dist/types/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PublicEndpointSpec } from './endpoints';
|
|
2
2
|
import type { HttpTransport } from '../http/transport';
|
|
3
|
-
import type {
|
|
3
|
+
import type { SecretStore } from '../secure/secret-store';
|
|
4
4
|
import type { ProfileStore } from '../secure/profile-store';
|
|
5
5
|
import type { DeviceNamespace } from '../namespaces/device';
|
|
6
6
|
import type { OrganizationNamespace } from '../namespaces/organization';
|
|
@@ -38,7 +38,7 @@ export interface XyteClientOptions {
|
|
|
38
38
|
device?: string;
|
|
39
39
|
};
|
|
40
40
|
profileStore?: ProfileStore;
|
|
41
|
-
|
|
41
|
+
secretStore?: SecretStore;
|
|
42
42
|
transport?: HttpTransport;
|
|
43
43
|
}
|
|
44
44
|
export interface XyteClient {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/types/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/types/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACrE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,CAAC;IACR,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE;QACL,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,eAAe,CAAC;IACxB,YAAY,EAAE,qBAAqB,CAAC;IACpC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAChG,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAAC;IAClD,aAAa,IAAI,kBAAkB,EAAE,CAAC;IACtC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;CACtE"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { XyteClient } from '../types/client';
|
|
2
2
|
import { INSPECT_DEEP_DIVE_SCHEMA_VERSION, INSPECT_FLEET_SCHEMA_VERSION, REPORT_SCHEMA_VERSION } from '../contracts/versions';
|
|
3
|
+
import { formatUtcForReport as formatUtcForReportFromLayout } from './report/time-format';
|
|
4
|
+
import { getWindowFocus as getWindowFocusFromTheme } from './report/theme';
|
|
3
5
|
interface StatusCounts {
|
|
4
6
|
[key: string]: number;
|
|
5
7
|
}
|
|
6
8
|
export interface FleetSnapshot {
|
|
7
9
|
generatedAtUtc: string;
|
|
8
10
|
tenantId: string;
|
|
11
|
+
tenantName?: string;
|
|
9
12
|
devices: any[];
|
|
10
13
|
spaces: any[];
|
|
11
14
|
incidents: any[];
|
|
@@ -39,6 +42,7 @@ export interface DeepDiveResult {
|
|
|
39
42
|
schemaVersion: typeof INSPECT_DEEP_DIVE_SCHEMA_VERSION;
|
|
40
43
|
generatedAtUtc: string;
|
|
41
44
|
tenantId: string;
|
|
45
|
+
tenantName?: string;
|
|
42
46
|
windowHours: number;
|
|
43
47
|
summary: string[];
|
|
44
48
|
topOfflineSpaces: Array<{
|
|
@@ -99,19 +103,14 @@ export interface FleetReportResult {
|
|
|
99
103
|
outputPath: string;
|
|
100
104
|
includeSensitive: boolean;
|
|
101
105
|
}
|
|
102
|
-
export declare function collectFleetSnapshot(client: XyteClient, tenantId: string): Promise<FleetSnapshot>;
|
|
106
|
+
export declare function collectFleetSnapshot(client: XyteClient, tenantId: string, tenantName?: string): Promise<FleetSnapshot>;
|
|
103
107
|
export declare function buildFleetInspect(snapshot: FleetSnapshot): FleetInspectResult;
|
|
104
108
|
export declare function formatFleetInspectAscii(result: FleetInspectResult): string;
|
|
105
109
|
export declare function buildDeepDive(snapshot: FleetSnapshot, windowHours?: number): DeepDiveResult;
|
|
106
110
|
export declare function formatDeepDiveAscii(result: DeepDiveResult): string;
|
|
107
111
|
export declare function formatDeepDiveMarkdown(result: DeepDiveResult, includeSensitive?: boolean): string;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
detail: string;
|
|
111
|
-
accent: string;
|
|
112
|
-
}
|
|
113
|
-
export declare function formatUtcForReport(value: unknown): string;
|
|
114
|
-
export declare function getWindowFocus(windowHours: number): WindowFocus;
|
|
112
|
+
export declare const formatUtcForReport: typeof formatUtcForReportFromLayout;
|
|
113
|
+
export declare const getWindowFocus: typeof getWindowFocusFromTheme;
|
|
115
114
|
export declare function generateFleetReport(args: {
|
|
116
115
|
deepDive: DeepDiveResult;
|
|
117
116
|
format: 'markdown' | 'pdf';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-insights.d.ts","sourceRoot":"","sources":["../../src/workflows/fleet-insights.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fleet-insights.d.ts","sourceRoot":"","sources":["../../src/workflows/fleet-insights.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWindowFocus = exports.formatUtcForReport = void 0;
|
|
6
4
|
exports.collectFleetSnapshot = collectFleetSnapshot;
|
|
7
5
|
exports.buildFleetInspect = buildFleetInspect;
|
|
8
6
|
exports.formatFleetInspectAscii = formatFleetInspectAscii;
|
|
9
7
|
exports.buildDeepDive = buildDeepDive;
|
|
10
8
|
exports.formatDeepDiveAscii = formatDeepDiveAscii;
|
|
11
9
|
exports.formatDeepDiveMarkdown = formatDeepDiveMarkdown;
|
|
12
|
-
exports.formatUtcForReport = formatUtcForReport;
|
|
13
|
-
exports.getWindowFocus = getWindowFocus;
|
|
14
10
|
exports.generateFleetReport = generateFleetReport;
|
|
15
11
|
const node_fs_1 = require("node:fs");
|
|
16
12
|
const node_path_1 = require("node:path");
|
|
17
|
-
const pdfkit_1 = __importDefault(require("pdfkit"));
|
|
18
13
|
const data_loaders_1 = require("../tui/data-loaders");
|
|
19
14
|
const versions_1 = require("../contracts/versions");
|
|
20
15
|
const tracing_1 = require("../observability/tracing");
|
|
16
|
+
const pdf_render_1 = require("./report/pdf-render");
|
|
17
|
+
const time_format_1 = require("./report/time-format");
|
|
18
|
+
const theme_1 = require("./report/theme");
|
|
21
19
|
function asRecord(value) {
|
|
22
20
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
23
21
|
return {};
|
|
@@ -173,7 +171,7 @@ async function loadAllSpaces(client, tenantId) {
|
|
|
173
171
|
const single = await client.organization.getSpaces({ tenantId });
|
|
174
172
|
return (0, data_loaders_1.extractArray)(single, ['spaces', 'data', 'items']);
|
|
175
173
|
}
|
|
176
|
-
async function collectFleetSnapshot(client, tenantId) {
|
|
174
|
+
async function collectFleetSnapshot(client, tenantId, tenantName) {
|
|
177
175
|
return (0, tracing_1.withSpan)('xyte.inspect.collect_snapshot', { 'xyte.tenant.id': tenantId }, async () => {
|
|
178
176
|
const [devices, spaces, incidentsRaw, orgTicketsRaw, partnerTicketsRaw] = await Promise.all([
|
|
179
177
|
loadAllDevices(client, tenantId),
|
|
@@ -190,6 +188,7 @@ async function collectFleetSnapshot(client, tenantId) {
|
|
|
190
188
|
return {
|
|
191
189
|
generatedAtUtc: new Date().toISOString(),
|
|
192
190
|
tenantId,
|
|
191
|
+
tenantName,
|
|
193
192
|
devices: stableSort(devices),
|
|
194
193
|
spaces: stableSort(spaces),
|
|
195
194
|
incidents: stableSort(incidents),
|
|
@@ -328,6 +327,7 @@ function buildDeepDive(snapshot, windowHours = 24) {
|
|
|
328
327
|
schemaVersion: versions_1.INSPECT_DEEP_DIVE_SCHEMA_VERSION,
|
|
329
328
|
generatedAtUtc: snapshot.generatedAtUtc,
|
|
330
329
|
tenantId: snapshot.tenantId,
|
|
330
|
+
tenantName: snapshot.tenantName,
|
|
331
331
|
windowHours,
|
|
332
332
|
summary,
|
|
333
333
|
topOfflineSpaces,
|
|
@@ -433,490 +433,8 @@ function formatDeepDiveMarkdown(result, includeSensitive = false) {
|
|
|
433
433
|
function ensureDir(filePath) {
|
|
434
434
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)((0, node_path_1.resolve)(filePath)), { recursive: true });
|
|
435
435
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const HEADER_TOP = 18;
|
|
439
|
-
const HEADER_HEIGHT = 68;
|
|
440
|
-
const FOOTER_HEIGHT = 22;
|
|
441
|
-
const CONTENT_TOP = HEADER_TOP + HEADER_HEIGHT + 16;
|
|
442
|
-
const SPACE_SM = 8;
|
|
443
|
-
const SPACE_MD = 12;
|
|
444
|
-
const SPACE_LG = 18;
|
|
445
|
-
const SPACE_XL = 24;
|
|
446
|
-
const FONT_H1 = 18;
|
|
447
|
-
const FONT_H2 = 13;
|
|
448
|
-
const FONT_BODY = 10;
|
|
449
|
-
const FONT_CAPTION = 9;
|
|
450
|
-
const TABLE_ROW_MIN = 22;
|
|
451
|
-
const TABLE_ROW_MAX = 220;
|
|
452
|
-
const TABLE_CELL_PAD_X = 6;
|
|
453
|
-
const TABLE_CELL_PAD_Y = 5;
|
|
454
|
-
function resolveLogoPath() {
|
|
455
|
-
const candidates = [
|
|
456
|
-
(0, node_path_1.resolve)(process.cwd(), 'assets/xyte-logo.png'),
|
|
457
|
-
(0, node_path_1.resolve)(__dirname, '../../assets/xyte-logo.png'),
|
|
458
|
-
(0, node_path_1.resolve)(__dirname, '../../../assets/xyte-logo.png')
|
|
459
|
-
];
|
|
460
|
-
return candidates.find((candidate) => (0, node_fs_1.existsSync)(candidate));
|
|
461
|
-
}
|
|
462
|
-
function formatTwoDigits(value) {
|
|
463
|
-
return String(value).padStart(2, '0');
|
|
464
|
-
}
|
|
465
|
-
function formatUtcForReport(value) {
|
|
466
|
-
const parsed = parseTimestamp(value);
|
|
467
|
-
if (!parsed) {
|
|
468
|
-
return identifier(value);
|
|
469
|
-
}
|
|
470
|
-
const y = parsed.getUTCFullYear();
|
|
471
|
-
const m = formatTwoDigits(parsed.getUTCMonth() + 1);
|
|
472
|
-
const d = formatTwoDigits(parsed.getUTCDate());
|
|
473
|
-
const hh = formatTwoDigits(parsed.getUTCHours());
|
|
474
|
-
const mm = formatTwoDigits(parsed.getUTCMinutes());
|
|
475
|
-
return `${y}-${m}-${d} ${hh}:${mm} UTC`;
|
|
476
|
-
}
|
|
477
|
-
function getWindowFocus(windowHours) {
|
|
478
|
-
if (windowHours <= 24) {
|
|
479
|
-
return {
|
|
480
|
-
label: 'Immediate churn',
|
|
481
|
-
detail: 'Prioritize active incident containment and hot spaces in the last day.',
|
|
482
|
-
accent: '#B45309'
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
if (windowHours <= 72) {
|
|
486
|
-
return {
|
|
487
|
-
label: 'Short-term Trend',
|
|
488
|
-
detail: 'Track repeat offenders and stabilize recurring high-churn spaces.',
|
|
489
|
-
accent: '#1D4ED8'
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
return {
|
|
493
|
-
label: 'Weekly concentration',
|
|
494
|
-
detail: 'Focus on sustained incident concentration and structural remediation.',
|
|
495
|
-
accent: '#166534'
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
function resetCursor(doc) {
|
|
499
|
-
doc.x = doc.page.margins.left;
|
|
500
|
-
doc.y = Math.max(doc.y, CONTENT_TOP);
|
|
501
|
-
}
|
|
502
|
-
function drawPdfHeader(doc, ctx) {
|
|
503
|
-
const left = doc.page.margins.left;
|
|
504
|
-
const right = doc.page.width - doc.page.margins.right;
|
|
505
|
-
const bandTop = HEADER_TOP;
|
|
506
|
-
const bandHeight = HEADER_HEIGHT;
|
|
507
|
-
doc.save();
|
|
508
|
-
doc.roundedRect(left, bandTop, right - left, bandHeight, 8).fillAndStroke('#E8F0FC', '#C2D5F3');
|
|
509
|
-
doc.restore();
|
|
510
|
-
if (ctx.logoPath) {
|
|
511
|
-
try {
|
|
512
|
-
doc.image(ctx.logoPath, left + 12, bandTop + 16, { fit: [110, 34] });
|
|
513
|
-
}
|
|
514
|
-
catch {
|
|
515
|
-
doc.font('Helvetica-Bold').fontSize(36).fillColor('#1459A6').text('XYTE', left + 12, bandTop + 12);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
else {
|
|
519
|
-
doc.font('Helvetica-Bold').fontSize(36).fillColor('#1459A6').text('XYTE', left + 12, bandTop + 12);
|
|
520
|
-
}
|
|
521
|
-
doc
|
|
522
|
-
.font('Helvetica-Bold')
|
|
523
|
-
.fontSize(FONT_H1)
|
|
524
|
-
.fillColor('#1A2332')
|
|
525
|
-
.text('Fleet Findings Report', left + 146, bandTop + 14, { width: right - left - 250, align: 'left' });
|
|
526
|
-
doc
|
|
527
|
-
.font('Helvetica')
|
|
528
|
-
.fontSize(FONT_BODY)
|
|
529
|
-
.fillColor('#415067')
|
|
530
|
-
.text(`Tenant: ${ctx.tenantId}`, left + 146, bandTop + 37, { width: right - left - 250, align: 'left' })
|
|
531
|
-
.text(`Generated: ${formatUtcForReport(ctx.generatedAtUtc)}`, left + 146, bandTop + 51, { width: right - left - 250, align: 'left' });
|
|
532
|
-
const badgeWidth = 165;
|
|
533
|
-
const badgeHeight = 28;
|
|
534
|
-
const badgeX = right - badgeWidth - 12;
|
|
535
|
-
const badgeY = bandTop + 20;
|
|
536
|
-
doc.save();
|
|
537
|
-
doc.roundedRect(badgeX, badgeY, badgeWidth, badgeHeight, 14).fill(ctx.windowFocus.accent);
|
|
538
|
-
doc.restore();
|
|
539
|
-
doc.font('Helvetica-Bold').fontSize(FONT_CAPTION).fillColor('#FFFFFF').text(`${ctx.windowHours}h • ${ctx.windowFocus.label}`, badgeX + 12, badgeY + 9, { width: badgeWidth - 24, align: 'center' });
|
|
540
|
-
}
|
|
541
|
-
function drawPdfFooter(doc, ctx, pageNumber, pageCount) {
|
|
542
|
-
const y = doc.page.height - doc.page.margins.bottom - FOOTER_HEIGHT + 10;
|
|
543
|
-
const left = doc.page.margins.left;
|
|
544
|
-
const right = doc.page.width - doc.page.margins.right;
|
|
545
|
-
doc.save();
|
|
546
|
-
doc.moveTo(left, y - 6).lineTo(right, y - 6).lineWidth(0.6).strokeColor('#D5DEE9').stroke();
|
|
547
|
-
doc.restore();
|
|
548
|
-
doc.font('Helvetica').fontSize(FONT_CAPTION).fillColor('#5B687B').text('Xyte Fleet Findings Report', left, y, { width: 220, align: 'left' });
|
|
549
|
-
doc.text(`${ctx.windowHours}h window`, left + 220, y, { width: 120, align: 'center' });
|
|
550
|
-
doc.text(`Page ${pageNumber} of ${pageCount}`, right - 120, y, { width: 120, align: 'right' });
|
|
551
|
-
}
|
|
552
|
-
function startReportPage(doc, ctx) {
|
|
553
|
-
doc.addPage();
|
|
554
|
-
drawPdfHeader(doc, ctx);
|
|
555
|
-
resetCursor(doc);
|
|
556
|
-
}
|
|
557
|
-
function ensurePageSpace(doc, ctx, minHeight) {
|
|
558
|
-
resetCursor(doc);
|
|
559
|
-
const bottom = doc.page.height - doc.page.margins.bottom - FOOTER_HEIGHT - SPACE_SM;
|
|
560
|
-
if (doc.y + minHeight <= bottom) {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
startReportPage(doc, ctx);
|
|
564
|
-
}
|
|
565
|
-
function drawSectionTitle(doc, ctx, title) {
|
|
566
|
-
ensurePageSpace(doc, ctx, 30);
|
|
567
|
-
resetCursor(doc);
|
|
568
|
-
doc.moveDown(0.2);
|
|
569
|
-
doc.font('Helvetica-Bold').fontSize(FONT_H2).fillColor('#182433').text(title, {
|
|
570
|
-
width: doc.page.width - doc.page.margins.left - doc.page.margins.right
|
|
571
|
-
});
|
|
572
|
-
const y = doc.y + 2;
|
|
573
|
-
doc.save();
|
|
574
|
-
doc.moveTo(doc.page.margins.left, y).lineTo(doc.page.width - doc.page.margins.right, y).lineWidth(0.8).strokeColor('#D4DEE8').stroke();
|
|
575
|
-
doc.restore();
|
|
576
|
-
doc.moveDown(0.2);
|
|
577
|
-
}
|
|
578
|
-
function drawWindowFocusStrip(doc, ctx) {
|
|
579
|
-
ensurePageSpace(doc, ctx, 56);
|
|
580
|
-
resetCursor(doc);
|
|
581
|
-
const x = doc.page.margins.left;
|
|
582
|
-
const y = doc.y;
|
|
583
|
-
const width = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
584
|
-
const height = 48;
|
|
585
|
-
doc.save();
|
|
586
|
-
doc.roundedRect(x, y, width, height, 7).fillAndStroke('#F3F7FC', '#D7E3F2');
|
|
587
|
-
doc.restore();
|
|
588
|
-
doc.font('Helvetica-Bold').fontSize(FONT_BODY).fillColor(ctx.windowFocus.accent).text('Window Focus', x + 12, y + 10);
|
|
589
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#243447').text(ctx.windowFocus.detail, x + 110, y + 10, {
|
|
590
|
-
width: width - 122
|
|
591
|
-
});
|
|
592
|
-
doc.y = y + height + SPACE_MD;
|
|
593
|
-
}
|
|
594
|
-
function drawKpiGrid(doc, ctx, cards) {
|
|
595
|
-
ensurePageSpace(doc, ctx, 106);
|
|
596
|
-
resetCursor(doc);
|
|
597
|
-
const startX = doc.page.margins.left;
|
|
598
|
-
const topY = doc.y;
|
|
599
|
-
const width = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
600
|
-
const gap = SPACE_SM;
|
|
601
|
-
const cardWidth = Math.floor((width - gap * 3) / 4);
|
|
602
|
-
const cardHeight = 84;
|
|
603
|
-
cards.slice(0, 4).forEach((card, index) => {
|
|
604
|
-
const x = startX + index * (cardWidth + gap);
|
|
605
|
-
const tone = card.tone === 'bad'
|
|
606
|
-
? { bg: '#FDEBEC', border: '#F7C4C7', value: '#A2282F' }
|
|
607
|
-
: card.tone === 'warn'
|
|
608
|
-
? { bg: '#FFF6E8', border: '#F7D9A6', value: '#9C5F08' }
|
|
609
|
-
: { bg: '#EEF6FF', border: '#C6E0FF', value: '#1459A6' };
|
|
610
|
-
doc.save();
|
|
611
|
-
doc.roundedRect(x, topY, cardWidth, cardHeight, 8).fillAndStroke(tone.bg, tone.border);
|
|
612
|
-
doc.restore();
|
|
613
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#4B5563').text(card.label, x + 10, topY + 13, {
|
|
614
|
-
width: cardWidth - 20
|
|
615
|
-
});
|
|
616
|
-
doc.font('Helvetica-Bold').fontSize(26).fillColor(tone.value).text(card.value, x + 10, topY + 37, {
|
|
617
|
-
width: cardWidth - 20
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
doc.y = topY + cardHeight + SPACE_LG;
|
|
621
|
-
}
|
|
622
|
-
function drawKeyFindings(doc, ctx, lines) {
|
|
623
|
-
const findings = lines.slice(0, 4);
|
|
624
|
-
if (!findings.length) {
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
ensurePageSpace(doc, ctx, 72);
|
|
628
|
-
resetCursor(doc);
|
|
629
|
-
const x = doc.page.margins.left;
|
|
630
|
-
const y = doc.y;
|
|
631
|
-
const width = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
632
|
-
doc.save();
|
|
633
|
-
doc.roundedRect(x, y, width, 56 + findings.length * 10, 8).fillAndStroke('#F8FAFD', '#DCE6F2');
|
|
634
|
-
doc.restore();
|
|
635
|
-
doc.font('Helvetica-Bold').fontSize(FONT_BODY).fillColor('#223245').text('Key Findings', x + 12, y + 10);
|
|
636
|
-
let cursorY = y + 26;
|
|
637
|
-
findings.forEach((line) => {
|
|
638
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#1F2A38').text(`- ${line}`, x + 12, cursorY, {
|
|
639
|
-
width: width - 24
|
|
640
|
-
});
|
|
641
|
-
cursorY += 14;
|
|
642
|
-
});
|
|
643
|
-
doc.y = y + 56 + findings.length * 10 + SPACE_LG;
|
|
644
|
-
}
|
|
645
|
-
function drawBullets(doc, ctx, lines) {
|
|
646
|
-
lines.forEach((line) => {
|
|
647
|
-
ensurePageSpace(doc, ctx, 20);
|
|
648
|
-
resetCursor(doc);
|
|
649
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#1F2937').text(`- ${line}`, {
|
|
650
|
-
width: doc.page.width - doc.page.margins.left - doc.page.margins.right
|
|
651
|
-
});
|
|
652
|
-
doc.moveDown(0.05);
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
function drawSpaceBars(doc, ctx, rows) {
|
|
656
|
-
if (!rows.length) {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
drawSectionTitle(doc, ctx, `${ctx.windowHours}h Churn Concentration (Top Spaces)`);
|
|
660
|
-
const chartRows = rows.slice(0, 5);
|
|
661
|
-
const maxValue = Math.max(...chartRows.map((row) => row.incidents), 1);
|
|
662
|
-
const pageWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
663
|
-
const labelWidth = 285;
|
|
664
|
-
const valueWidth = 50;
|
|
665
|
-
const barWidth = pageWidth - labelWidth - valueWidth - 20;
|
|
666
|
-
chartRows.forEach((row) => {
|
|
667
|
-
ensurePageSpace(doc, ctx, 24);
|
|
668
|
-
resetCursor(doc);
|
|
669
|
-
const y = doc.y;
|
|
670
|
-
const x = doc.page.margins.left;
|
|
671
|
-
const ratio = row.incidents / maxValue;
|
|
672
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#1F2937').text(row.space, x, y + 5, {
|
|
673
|
-
width: labelWidth - 8,
|
|
674
|
-
ellipsis: true
|
|
675
|
-
});
|
|
676
|
-
doc.save();
|
|
677
|
-
doc.roundedRect(x + labelWidth, y + 8, barWidth, 9, 3).fill('#E6ECF5');
|
|
678
|
-
doc.roundedRect(x + labelWidth, y + 8, Math.max(8, barWidth * ratio), 9, 3).fill('#3B82F6');
|
|
679
|
-
doc.restore();
|
|
680
|
-
doc.font('Helvetica-Bold').fontSize(FONT_BODY).fillColor('#1F2937').text(String(row.incidents), x + labelWidth + barWidth + 8, y + 5, {
|
|
681
|
-
width: valueWidth,
|
|
682
|
-
align: 'right'
|
|
683
|
-
});
|
|
684
|
-
doc.y = y + 22;
|
|
685
|
-
});
|
|
686
|
-
doc.moveDown(0.35);
|
|
687
|
-
}
|
|
688
|
-
function normalizeColumns(columns, availableWidth) {
|
|
689
|
-
const total = columns.reduce((sum, column) => sum + column.width, 0);
|
|
690
|
-
if (Math.abs(total - availableWidth) <= 1) {
|
|
691
|
-
return columns;
|
|
692
|
-
}
|
|
693
|
-
if (total > availableWidth) {
|
|
694
|
-
const ratio = availableWidth / total;
|
|
695
|
-
const scaled = columns.map((column) => ({ ...column, width: Math.floor(column.width * ratio) }));
|
|
696
|
-
const scaledTotal = scaled.reduce((sum, column) => sum + column.width, 0);
|
|
697
|
-
scaled[0].width += availableWidth - scaledTotal;
|
|
698
|
-
return scaled;
|
|
699
|
-
}
|
|
700
|
-
const grown = columns.map((column) => ({ ...column }));
|
|
701
|
-
const flexIndex = grown.findIndex((column) => column.wrap !== false);
|
|
702
|
-
const target = flexIndex === -1 ? 0 : flexIndex;
|
|
703
|
-
grown[target].width += availableWidth - total;
|
|
704
|
-
return grown;
|
|
705
|
-
}
|
|
706
|
-
function measureTableRowHeight(doc, columns, row) {
|
|
707
|
-
doc.font('Helvetica').fontSize(FONT_BODY);
|
|
708
|
-
const lineHeight = doc.currentLineHeight();
|
|
709
|
-
let maxHeight = lineHeight;
|
|
710
|
-
row.forEach((cell, index) => {
|
|
711
|
-
const column = columns[index];
|
|
712
|
-
const innerWidth = Math.max(20, column.width - TABLE_CELL_PAD_X * 2);
|
|
713
|
-
if (column.wrap === false) {
|
|
714
|
-
maxHeight = Math.max(maxHeight, lineHeight);
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
const measured = doc.heightOfString(cell, {
|
|
718
|
-
width: innerWidth,
|
|
719
|
-
align: column.align ?? 'left'
|
|
720
|
-
});
|
|
721
|
-
maxHeight = Math.max(maxHeight, measured);
|
|
722
|
-
});
|
|
723
|
-
const rowHeight = maxHeight + TABLE_CELL_PAD_Y * 2;
|
|
724
|
-
return Math.min(TABLE_ROW_MAX, Math.max(TABLE_ROW_MIN, rowHeight));
|
|
725
|
-
}
|
|
726
|
-
function drawTable(doc, ctx, args) {
|
|
727
|
-
const tableLeft = doc.page.margins.left;
|
|
728
|
-
const availableWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
729
|
-
const columns = normalizeColumns(args.columns, availableWidth);
|
|
730
|
-
const headerHeight = 24;
|
|
731
|
-
const continuationTitle = `${args.title} (cont.)`;
|
|
732
|
-
const drawHeader = () => {
|
|
733
|
-
ensurePageSpace(doc, ctx, headerHeight + 6);
|
|
734
|
-
resetCursor(doc);
|
|
735
|
-
const y = doc.y;
|
|
736
|
-
let x = tableLeft;
|
|
737
|
-
columns.forEach((column) => {
|
|
738
|
-
doc.save();
|
|
739
|
-
doc.rect(x, y, column.width, headerHeight).fillAndStroke('#E8EEF6', '#CAD7E8');
|
|
740
|
-
doc.restore();
|
|
741
|
-
doc.font('Helvetica-Bold').fontSize(FONT_BODY).fillColor('#1F2937').text(column.header, x + TABLE_CELL_PAD_X, y + 6, {
|
|
742
|
-
width: column.width - TABLE_CELL_PAD_X * 2,
|
|
743
|
-
align: column.align ?? 'left',
|
|
744
|
-
ellipsis: true
|
|
745
|
-
});
|
|
746
|
-
x += column.width;
|
|
747
|
-
});
|
|
748
|
-
doc.y = y + headerHeight;
|
|
749
|
-
};
|
|
750
|
-
if (!args.rows.length) {
|
|
751
|
-
ensurePageSpace(doc, ctx, 32 + headerHeight);
|
|
752
|
-
drawSectionTitle(doc, ctx, args.title);
|
|
753
|
-
ensurePageSpace(doc, ctx, 24);
|
|
754
|
-
resetCursor(doc);
|
|
755
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#475569').text(args.emptyMessage ?? 'No data available.', {
|
|
756
|
-
width: availableWidth
|
|
757
|
-
});
|
|
758
|
-
doc.moveDown(0.5);
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
const firstRowHeight = measureTableRowHeight(doc, columns, args.rows[0]);
|
|
762
|
-
ensurePageSpace(doc, ctx, 34 + headerHeight + firstRowHeight);
|
|
763
|
-
drawSectionTitle(doc, ctx, args.title);
|
|
764
|
-
drawHeader();
|
|
765
|
-
args.rows.forEach((row) => {
|
|
766
|
-
const rowHeight = measureTableRowHeight(doc, columns, row);
|
|
767
|
-
const bottom = doc.page.height - doc.page.margins.bottom - FOOTER_HEIGHT - SPACE_SM;
|
|
768
|
-
if (doc.y + rowHeight > bottom) {
|
|
769
|
-
startReportPage(doc, ctx);
|
|
770
|
-
ensurePageSpace(doc, ctx, 34 + headerHeight + Math.min(rowHeight, 60));
|
|
771
|
-
drawSectionTitle(doc, ctx, continuationTitle);
|
|
772
|
-
drawHeader();
|
|
773
|
-
}
|
|
774
|
-
const y = doc.y;
|
|
775
|
-
let x = tableLeft;
|
|
776
|
-
row.forEach((cell, index) => {
|
|
777
|
-
const column = columns[index];
|
|
778
|
-
doc.save();
|
|
779
|
-
doc.rect(x, y, column.width, rowHeight).fillAndStroke('#FFFFFF', '#E3EAF3');
|
|
780
|
-
doc.restore();
|
|
781
|
-
doc.font('Helvetica').fontSize(FONT_BODY).fillColor('#0F172A').text(cell, x + TABLE_CELL_PAD_X, y + TABLE_CELL_PAD_Y, {
|
|
782
|
-
width: column.width - TABLE_CELL_PAD_X * 2,
|
|
783
|
-
height: rowHeight - TABLE_CELL_PAD_Y * 2,
|
|
784
|
-
align: column.align ?? 'left',
|
|
785
|
-
lineBreak: column.wrap !== false,
|
|
786
|
-
ellipsis: column.wrap === false
|
|
787
|
-
});
|
|
788
|
-
x += column.width;
|
|
789
|
-
});
|
|
790
|
-
doc.y = y + rowHeight;
|
|
791
|
-
});
|
|
792
|
-
doc.moveDown(0.45);
|
|
793
|
-
}
|
|
794
|
-
function renderBrandedPdfReport(deepDive, outputPath, includeSensitive) {
|
|
795
|
-
return new Promise((resolvePromise, rejectPromise) => {
|
|
796
|
-
ensureDir(outputPath);
|
|
797
|
-
const ctx = {
|
|
798
|
-
tenantId: deepDive.tenantId,
|
|
799
|
-
generatedAtUtc: deepDive.generatedAtUtc,
|
|
800
|
-
windowHours: deepDive.windowHours,
|
|
801
|
-
windowFocus: getWindowFocus(deepDive.windowHours),
|
|
802
|
-
logoPath: resolveLogoPath()
|
|
803
|
-
};
|
|
804
|
-
const doc = new pdfkit_1.default({
|
|
805
|
-
size: 'LETTER',
|
|
806
|
-
margins: {
|
|
807
|
-
left: PAGE_MARGIN_X,
|
|
808
|
-
right: PAGE_MARGIN_X,
|
|
809
|
-
top: PAGE_MARGIN_Y,
|
|
810
|
-
bottom: PAGE_MARGIN_Y
|
|
811
|
-
},
|
|
812
|
-
bufferPages: true
|
|
813
|
-
});
|
|
814
|
-
const stream = doc.pipe((0, node_fs_1.createWriteStream)(outputPath));
|
|
815
|
-
stream.on('finish', () => resolvePromise());
|
|
816
|
-
stream.on('error', (error) => rejectPromise(error));
|
|
817
|
-
drawPdfHeader(doc, ctx);
|
|
818
|
-
resetCursor(doc);
|
|
819
|
-
drawKpiGrid(doc, ctx, [
|
|
820
|
-
{ label: 'Active incidents', value: String(deepDive.activeIncidentAging.length), tone: deepDive.activeIncidentAging.length > 0 ? 'warn' : 'normal' },
|
|
821
|
-
{ label: `${deepDive.windowHours}h churn`, value: String(deepDive.churn24h.incidents), tone: deepDive.churn24h.incidents > 0 ? 'warn' : 'normal' },
|
|
822
|
-
{ label: 'Open tickets', value: String(deepDive.ticketPosture.openTickets), tone: deepDive.ticketPosture.openTickets > 0 ? 'warn' : 'normal' },
|
|
823
|
-
{
|
|
824
|
-
label: 'Data mismatches',
|
|
825
|
-
value: String(deepDive.dataQuality.statusMismatches.length),
|
|
826
|
-
tone: deepDive.dataQuality.statusMismatches.length > 0 ? 'bad' : 'normal'
|
|
827
|
-
}
|
|
828
|
-
]);
|
|
829
|
-
drawWindowFocusStrip(doc, ctx);
|
|
830
|
-
drawKeyFindings(doc, ctx, deepDive.summary);
|
|
831
|
-
drawSectionTitle(doc, ctx, 'Executive Summary');
|
|
832
|
-
drawBullets(doc, ctx, deepDive.summary);
|
|
833
|
-
doc.moveDown(0.35);
|
|
834
|
-
drawSpaceBars(doc, ctx, deepDive.churn24h.bySpace);
|
|
835
|
-
drawTable(doc, ctx, {
|
|
836
|
-
title: 'Top Spaces by Offline Devices',
|
|
837
|
-
columns: [
|
|
838
|
-
{ header: 'Space', width: 370, wrap: true },
|
|
839
|
-
{ header: 'Offline', width: 90, align: 'right', wrap: false },
|
|
840
|
-
{ header: 'Share', width: 90, align: 'right', wrap: false }
|
|
841
|
-
],
|
|
842
|
-
rows: deepDive.topOfflineSpaces.map((row) => [row.space, String(row.offlineDevices), `${row.shareOfOfflinePct}%`]),
|
|
843
|
-
emptyMessage: 'No offline spaces found.'
|
|
844
|
-
});
|
|
845
|
-
drawTable(doc, ctx, {
|
|
846
|
-
title: 'Top Devices by Incident Volume',
|
|
847
|
-
columns: [
|
|
848
|
-
{ header: 'Device', width: 370, wrap: true },
|
|
849
|
-
{ header: 'Incidents', width: 90, align: 'right', wrap: false },
|
|
850
|
-
{ header: 'Active', width: 90, align: 'right', wrap: false }
|
|
851
|
-
],
|
|
852
|
-
rows: deepDive.topIncidentDevices.map((row) => [row.device, String(row.incidentCount), String(row.activeIncidents)]),
|
|
853
|
-
emptyMessage: 'No incident device concentration detected.'
|
|
854
|
-
});
|
|
855
|
-
drawTable(doc, ctx, {
|
|
856
|
-
title: 'Active Incident Aging',
|
|
857
|
-
columns: [
|
|
858
|
-
{ header: 'Device', width: 120, wrap: true },
|
|
859
|
-
{ header: 'Space', width: 230, wrap: true },
|
|
860
|
-
{ header: 'Age (h)', width: 70, align: 'right', wrap: false },
|
|
861
|
-
{ header: 'Created At', width: 130, wrap: false }
|
|
862
|
-
],
|
|
863
|
-
rows: deepDive.activeIncidentAging.slice(0, 16).map((row) => [row.device, row.space, String(row.ageHours), formatUtcForReport(row.createdAtUtc)]),
|
|
864
|
-
emptyMessage: 'No active incidents.'
|
|
865
|
-
});
|
|
866
|
-
drawTable(doc, ctx, {
|
|
867
|
-
title: `${deepDive.windowHours}-Hour Churn by Space`,
|
|
868
|
-
columns: [
|
|
869
|
-
{ header: 'Space', width: 450, wrap: true },
|
|
870
|
-
{ header: 'Incidents', width: 100, align: 'right', wrap: false }
|
|
871
|
-
],
|
|
872
|
-
rows: deepDive.churn24h.bySpace.map((row) => [row.space, String(row.incidents)]),
|
|
873
|
-
emptyMessage: 'No churn events in this window.'
|
|
874
|
-
});
|
|
875
|
-
drawTable(doc, ctx, {
|
|
876
|
-
title: 'Oldest Open Tickets',
|
|
877
|
-
columns: [
|
|
878
|
-
{ header: 'Ticket', width: 88, wrap: false },
|
|
879
|
-
{ header: 'Title', width: 182, wrap: true },
|
|
880
|
-
{ header: 'Age (h)', width: 62, align: 'right', wrap: false },
|
|
881
|
-
{ header: 'Device', width: 88, wrap: false },
|
|
882
|
-
{ header: 'Created At', width: 130, wrap: false }
|
|
883
|
-
],
|
|
884
|
-
rows: deepDive.ticketPosture.oldestOpenTickets.slice(0, 12).map((row) => [
|
|
885
|
-
redactSensitive(row.ticketId, includeSensitive),
|
|
886
|
-
row.title,
|
|
887
|
-
String(row.ageHours),
|
|
888
|
-
redactSensitive(row.deviceId, includeSensitive),
|
|
889
|
-
formatUtcForReport(row.createdAtUtc)
|
|
890
|
-
]),
|
|
891
|
-
emptyMessage: 'No open tickets.'
|
|
892
|
-
});
|
|
893
|
-
if (deepDive.dataQuality.statusMismatches.length) {
|
|
894
|
-
drawTable(doc, ctx, {
|
|
895
|
-
title: 'Data Quality: Status Mismatches',
|
|
896
|
-
columns: [
|
|
897
|
-
{ header: 'Device', width: 120, wrap: true },
|
|
898
|
-
{ header: 'status', width: 70, wrap: false },
|
|
899
|
-
{ header: 'state.status', width: 90, wrap: false },
|
|
900
|
-
{ header: 'Last Seen', width: 130, wrap: false },
|
|
901
|
-
{ header: 'Space', width: 160, wrap: true }
|
|
902
|
-
],
|
|
903
|
-
rows: deepDive.dataQuality.statusMismatches.map((row) => [
|
|
904
|
-
row.device,
|
|
905
|
-
row.status,
|
|
906
|
-
row.stateStatus,
|
|
907
|
-
formatUtcForReport(row.lastSeen),
|
|
908
|
-
row.space
|
|
909
|
-
])
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
const pages = doc.bufferedPageRange();
|
|
913
|
-
for (let index = pages.start; index < pages.start + pages.count; index += 1) {
|
|
914
|
-
doc.switchToPage(index);
|
|
915
|
-
drawPdfFooter(doc, ctx, index - pages.start + 1, pages.count);
|
|
916
|
-
}
|
|
917
|
-
doc.end();
|
|
918
|
-
});
|
|
919
|
-
}
|
|
436
|
+
exports.formatUtcForReport = time_format_1.formatUtcForReport;
|
|
437
|
+
exports.getWindowFocus = theme_1.getWindowFocus;
|
|
920
438
|
async function generateFleetReport(args) {
|
|
921
439
|
const markdown = formatDeepDiveMarkdown(args.deepDive, args.includeSensitive);
|
|
922
440
|
ensureDir(args.outPath);
|
|
@@ -924,7 +442,7 @@ async function generateFleetReport(args) {
|
|
|
924
442
|
(0, node_fs_1.writeFileSync)(args.outPath, markdown, 'utf8');
|
|
925
443
|
}
|
|
926
444
|
else {
|
|
927
|
-
await renderBrandedPdfReport(args.deepDive, args.outPath, args.includeSensitive);
|
|
445
|
+
await (0, pdf_render_1.renderBrandedPdfReport)(args.deepDive, args.outPath, args.includeSensitive);
|
|
928
446
|
}
|
|
929
447
|
return {
|
|
930
448
|
schemaVersion: versions_1.REPORT_SCHEMA_VERSION,
|