@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.
Files changed (91) hide show
  1. package/README.md +27 -5
  2. package/dist/cli/index.d.ts +2 -2
  3. package/dist/cli/index.d.ts.map +1 -1
  4. package/dist/cli/index.js +52 -44
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/client/create-client.js +14 -14
  7. package/dist/client/create-client.js.map +1 -1
  8. package/dist/config/readiness.d.ts +2 -2
  9. package/dist/config/readiness.d.ts.map +1 -1
  10. package/dist/config/readiness.js +1 -1
  11. package/dist/config/readiness.js.map +1 -1
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +5 -4
  15. package/dist/index.js.map +1 -1
  16. package/dist/mcp/server.d.ts +2 -2
  17. package/dist/mcp/server.d.ts.map +1 -1
  18. package/dist/mcp/server.js +3 -3
  19. package/dist/mcp/server.js.map +1 -1
  20. package/dist/namespaces/device.d.ts +1 -0
  21. package/dist/namespaces/device.d.ts.map +1 -1
  22. package/dist/namespaces/device.js +1 -0
  23. package/dist/namespaces/device.js.map +1 -1
  24. package/dist/namespaces/organization.d.ts +1 -0
  25. package/dist/namespaces/organization.d.ts.map +1 -1
  26. package/dist/namespaces/organization.js +1 -0
  27. package/dist/namespaces/organization.js.map +1 -1
  28. package/dist/secure/{keychain.d.ts → secret-store.d.ts} +16 -4
  29. package/dist/secure/secret-store.d.ts.map +1 -0
  30. package/dist/secure/secret-store.js +140 -0
  31. package/dist/secure/secret-store.js.map +1 -0
  32. package/dist/spec/public-endpoints.json +37 -0
  33. package/dist/tui/app.d.ts +2 -2
  34. package/dist/tui/app.d.ts.map +1 -1
  35. package/dist/tui/app.js +5 -5
  36. package/dist/tui/app.js.map +1 -1
  37. package/dist/tui/headless-renderer.d.ts +2 -2
  38. package/dist/tui/headless-renderer.d.ts.map +1 -1
  39. package/dist/tui/headless-renderer.js +4 -4
  40. package/dist/tui/headless-renderer.js.map +1 -1
  41. package/dist/tui/key-wizard.d.ts +1 -1
  42. package/dist/tui/key-wizard.d.ts.map +1 -1
  43. package/dist/tui/key-wizard.js +2 -2
  44. package/dist/tui/key-wizard.js.map +1 -1
  45. package/dist/tui/screens/config.js +6 -6
  46. package/dist/tui/screens/config.js.map +1 -1
  47. package/dist/tui/types.d.ts +2 -2
  48. package/dist/tui/types.d.ts.map +1 -1
  49. package/dist/types/client.d.ts +2 -2
  50. package/dist/types/client.d.ts.map +1 -1
  51. package/dist/workflows/fleet-insights.d.ts +7 -8
  52. package/dist/workflows/fleet-insights.d.ts.map +1 -1
  53. package/dist/workflows/fleet-insights.js +10 -492
  54. package/dist/workflows/fleet-insights.js.map +1 -1
  55. package/dist/workflows/report/font-asset.d.ts +8 -0
  56. package/dist/workflows/report/font-asset.d.ts.map +1 -0
  57. package/dist/workflows/report/font-asset.js +1462 -0
  58. package/dist/workflows/report/font-asset.js.map +1 -0
  59. package/dist/workflows/report/logo-asset.d.ts +7 -0
  60. package/dist/workflows/report/logo-asset.d.ts.map +1 -0
  61. package/dist/workflows/report/logo-asset.js +707 -0
  62. package/dist/workflows/report/logo-asset.js.map +1 -0
  63. package/dist/workflows/report/pdf-layout.d.ts +59 -0
  64. package/dist/workflows/report/pdf-layout.d.ts.map +1 -0
  65. package/dist/workflows/report/pdf-layout.js +388 -0
  66. package/dist/workflows/report/pdf-layout.js.map +1 -0
  67. package/dist/workflows/report/pdf-render.d.ts +3 -0
  68. package/dist/workflows/report/pdf-render.d.ts.map +1 -0
  69. package/dist/workflows/report/pdf-render.js +221 -0
  70. package/dist/workflows/report/pdf-render.js.map +1 -0
  71. package/dist/workflows/report/pdf-table.d.ts +33 -0
  72. package/dist/workflows/report/pdf-table.d.ts.map +1 -0
  73. package/dist/workflows/report/pdf-table.js +213 -0
  74. package/dist/workflows/report/pdf-table.js.map +1 -0
  75. package/dist/workflows/report/theme.d.ts +53 -0
  76. package/dist/workflows/report/theme.d.ts.map +1 -0
  77. package/dist/workflows/report/theme.js +95 -0
  78. package/dist/workflows/report/theme.js.map +1 -0
  79. package/dist/workflows/report/time-format.d.ts +5 -0
  80. package/dist/workflows/report/time-format.d.ts.map +1 -0
  81. package/dist/workflows/report/time-format.js +112 -0
  82. package/dist/workflows/report/time-format.js.map +1 -0
  83. package/docs/schemas/inspect-deep-dive.v1.schema.json +3 -0
  84. package/package.json +5 -1
  85. package/skills/xyte-cli/SKILL.md +7 -2
  86. package/skills/xyte-cli/agents/openai.yaml +1 -1
  87. package/skills/xyte-cli/references/endpoints.md +23 -0
  88. package/skills/xyte-cli/scripts/check_headless.sh +1 -1
  89. package/dist/secure/keychain.d.ts.map +0 -1
  90. package/dist/secure/keychain.js +0 -170
  91. package/dist/secure/keychain.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import type { PublicEndpointSpec } from './endpoints';
2
2
  import type { HttpTransport } from '../http/transport';
3
- import type { KeychainStore } from '../secure/keychain';
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
- keychain?: KeychainStore;
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,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,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,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,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
+ {"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
- interface WindowFocus {
109
- label: string;
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":"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"}
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
- const PAGE_MARGIN_X = 46;
437
- const PAGE_MARGIN_Y = 42;
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,