@cogenta/fleet 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/assert.d.ts +9 -0
- package/dist/agent/assert.d.ts.map +1 -0
- package/dist/agent/assert.js +64 -0
- package/dist/agent/assert.js.map +1 -0
- package/dist/agent/build.d.ts +21 -0
- package/dist/agent/build.d.ts.map +1 -0
- package/dist/agent/build.js +43 -0
- package/dist/agent/build.js.map +1 -0
- package/dist/agent/commands.d.ts +39 -0
- package/dist/agent/commands.d.ts.map +1 -0
- package/dist/agent/commands.js +43 -0
- package/dist/agent/commands.js.map +1 -0
- package/dist/agent/rollback-handler.d.ts +28 -0
- package/dist/agent/rollback-handler.d.ts.map +1 -0
- package/dist/agent/rollback-handler.js +52 -0
- package/dist/agent/rollback-handler.js.map +1 -0
- package/dist/agent/sign.d.ts +18 -0
- package/dist/agent/sign.d.ts.map +1 -0
- package/dist/agent/sign.js +19 -0
- package/dist/agent/sign.js.map +1 -0
- package/dist/agent/types.d.ts +86 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +2 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/alerts/alerts.d.ts +42 -0
- package/dist/alerts/alerts.d.ts.map +1 -0
- package/dist/alerts/alerts.js +98 -0
- package/dist/alerts/alerts.js.map +1 -0
- package/dist/alerts/conditions.d.ts +25 -0
- package/dist/alerts/conditions.d.ts.map +1 -0
- package/dist/alerts/conditions.js +28 -0
- package/dist/alerts/conditions.js.map +1 -0
- package/dist/alerts/tables.d.ts +13 -0
- package/dist/alerts/tables.d.ts.map +1 -0
- package/dist/alerts/tables.js +27 -0
- package/dist/alerts/tables.js.map +1 -0
- package/dist/control/commands.d.ts +38 -0
- package/dist/control/commands.d.ts.map +1 -0
- package/dist/control/commands.js +51 -0
- package/dist/control/commands.js.map +1 -0
- package/dist/control/identity.d.ts +13 -0
- package/dist/control/identity.d.ts.map +1 -0
- package/dist/control/identity.js +5 -0
- package/dist/control/identity.js.map +1 -0
- package/dist/control/ingest.d.ts +46 -0
- package/dist/control/ingest.d.ts.map +1 -0
- package/dist/control/ingest.js +56 -0
- package/dist/control/ingest.js.map +1 -0
- package/dist/control/risk.d.ts +66 -0
- package/dist/control/risk.d.ts.map +1 -0
- package/dist/control/risk.js +130 -0
- package/dist/control/risk.js.map +1 -0
- package/dist/control/state.d.ts +19 -0
- package/dist/control/state.d.ts.map +1 -0
- package/dist/control/state.js +67 -0
- package/dist/control/state.js.map +1 -0
- package/dist/control/tables.d.ts +18 -0
- package/dist/control/tables.d.ts.map +1 -0
- package/dist/control/tables.js +58 -0
- package/dist/control/tables.js.map +1 -0
- package/dist/enrollment/store.d.ts +84 -0
- package/dist/enrollment/store.d.ts.map +1 -0
- package/dist/enrollment/store.js +81 -0
- package/dist/enrollment/store.js.map +1 -0
- package/dist/enrollment/tables.d.ts +20 -0
- package/dist/enrollment/tables.d.ts.map +1 -0
- package/dist/enrollment/tables.js +55 -0
- package/dist/enrollment/tables.js.map +1 -0
- package/dist/enrollment/tokens.d.ts +4 -0
- package/dist/enrollment/tokens.d.ts.map +1 -0
- package/dist/enrollment/tokens.js +20 -0
- package/dist/enrollment/tokens.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory/drift.d.ts +73 -0
- package/dist/inventory/drift.d.ts.map +1 -0
- package/dist/inventory/drift.js +91 -0
- package/dist/inventory/drift.js.map +1 -0
- package/dist/reporting/report.d.ts +65 -0
- package/dist/reporting/report.d.ts.map +1 -0
- package/dist/reporting/report.js +137 -0
- package/dist/reporting/report.js.map +1 -0
- package/dist/reporting/schedule.d.ts +10 -0
- package/dist/reporting/schedule.d.ts.map +1 -0
- package/dist/reporting/schedule.js +34 -0
- package/dist/reporting/schedule.js.map +1 -0
- package/dist/reporting/tables.d.ts +11 -0
- package/dist/reporting/tables.d.ts.map +1 -0
- package/dist/reporting/tables.js +22 -0
- package/dist/reporting/tables.js.map +1 -0
- package/dist/rollout/campaign.d.ts +103 -0
- package/dist/rollout/campaign.d.ts.map +1 -0
- package/dist/rollout/campaign.js +258 -0
- package/dist/rollout/campaign.js.map +1 -0
- package/dist/rollout/rollback.d.ts +51 -0
- package/dist/rollout/rollback.d.ts.map +1 -0
- package/dist/rollout/rollback.js +60 -0
- package/dist/rollout/rollback.js.map +1 -0
- package/dist/rollout/tables.d.ts +19 -0
- package/dist/rollout/tables.d.ts.map +1 -0
- package/dist/rollout/tables.js +65 -0
- package/dist/rollout/tables.js.map +1 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export { assertNoForbiddenFields } from './agent/assert.js';
|
|
2
|
+
export { fingerprintSbom, summarizeAdminAccounts } from './agent/build.js';
|
|
3
|
+
export type { FleetCommandDispatchResult, FleetCommandHandlers, FleetCommandVerification, } from './agent/commands.js';
|
|
4
|
+
export { dispatchFleetCommand, verifyFleetCommand } from './agent/commands.js';
|
|
5
|
+
export type { RollbackIntent } from './agent/rollback-handler.js';
|
|
6
|
+
export { createRollbackIntentHandler } from './agent/rollback-handler.js';
|
|
7
|
+
export type { SignedTelemetry } from './agent/sign.js';
|
|
8
|
+
export { signTelemetryPayload, verifyTelemetrySignature } from './agent/sign.js';
|
|
9
|
+
export type { AdminAccountsSummary, AggregatedErrorsSummary, AvailabilitySummary, BackupSummary, CertificateExpirySummary, InstalledVersions, OpenCveSummary, TelemetryPayload, } from './agent/types.js';
|
|
10
|
+
export type { AlertSender } from './alerts/alerts.js';
|
|
11
|
+
export { detectCampaignHaltedAlert, detectCriticalRiskAlert, detectSiteSilentAlert, dispatchAlert, raiseIfNew, } from './alerts/alerts.js';
|
|
12
|
+
export type { AlertConditionStore, AlertConditionType } from './alerts/conditions.js';
|
|
13
|
+
export { createAlertConditionStore } from './alerts/conditions.js';
|
|
14
|
+
export { ALERT_TABLES, ensureAlertTables } from './alerts/tables.js';
|
|
15
|
+
export type { CommandQueueStore, FleetCommand, FleetCommandAction, SignedFleetCommand, } from './control/commands.js';
|
|
16
|
+
export { createCommandQueueStore, FLEET_COMMAND_ACTIONS } from './control/commands.js';
|
|
17
|
+
export type { ControlPlaneIdentity } from './control/identity.js';
|
|
18
|
+
export { generateControlPlaneIdentity } from './control/identity.js';
|
|
19
|
+
export type { IngestResult } from './control/ingest.js';
|
|
20
|
+
export { ingestTelemetry } from './control/ingest.js';
|
|
21
|
+
export type { FleetFilter, RiskReason, RiskTier, SiteRisk } from './control/risk.js';
|
|
22
|
+
export { computeSiteRisk, filterRisks, groupRisksByClient, rankSitesByRisk, } from './control/risk.js';
|
|
23
|
+
export type { SiteStateStore, TelemetrySnapshot } from './control/state.js';
|
|
24
|
+
export { createSiteStateStore } from './control/state.js';
|
|
25
|
+
export { CONTROL_TABLES, ensureControlTables } from './control/tables.js';
|
|
26
|
+
export type { EnrollmentStore, PairingConsumeResult, PairingToken, SiteRegistration, } from './enrollment/store.js';
|
|
27
|
+
export { createEnrollmentStore } from './enrollment/store.js';
|
|
28
|
+
export { ensureFleetTables, FLEET_TABLES } from './enrollment/tables.js';
|
|
29
|
+
export { generatePairingToken, hashPairingToken } from './enrollment/tokens.js';
|
|
30
|
+
export type { DriftDirection, DriftEntry, FleetBaseline, InventoryComponent, InventoryComponentKind, SiteInventory, } from './inventory/drift.js';
|
|
31
|
+
export { computeFleetBaseline, detectDrift, extractInventory } from './inventory/drift.js';
|
|
32
|
+
export type { ClientReport, ReportField } from './reporting/report.js';
|
|
33
|
+
export { assembleClientReport, renderClientReport } from './reporting/report.js';
|
|
34
|
+
export type { ReportScheduleStore } from './reporting/schedule.js';
|
|
35
|
+
export { createReportScheduleStore, isReportDue } from './reporting/schedule.js';
|
|
36
|
+
export { ensureReportingTables, REPORTING_TABLES } from './reporting/tables.js';
|
|
37
|
+
export type { CampaignRecord, CampaignStatus, CampaignWave, CampaignWaveLabel, RolloutCampaignStore, SiteRolloutRecord, SiteRolloutStatus, StartCampaignInput, } from './rollout/campaign.js';
|
|
38
|
+
export { createRolloutCampaignStore, orderSitesForCanary, planWaves, } from './rollout/campaign.js';
|
|
39
|
+
export type { RollbackCandidate } from './rollout/rollback.js';
|
|
40
|
+
export { listRollbackCandidates, triggerRollback } from './rollout/rollback.js';
|
|
41
|
+
export { ensureRolloutTables, ROLLOUT_TABLES } from './rollout/tables.js';
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC1E,YAAY,EACV,0BAA0B,EAC1B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC9E,YAAY,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAChF,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACnB,aAAa,EACb,wBAAwB,EACxB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,aAAa,EACb,UAAU,GACX,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACpE,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACpF,OAAO,EACL,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzE,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,YAAY,EACZ,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC/E,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,GACd,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC1F,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACtE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAChF,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC/E,YAAY,EACV,cAAc,EACd,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,SAAS,GACV,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { assertNoForbiddenFields } from './agent/assert.js';
|
|
2
|
+
export { fingerprintSbom, summarizeAdminAccounts } from './agent/build.js';
|
|
3
|
+
export { dispatchFleetCommand, verifyFleetCommand } from './agent/commands.js';
|
|
4
|
+
export { createRollbackIntentHandler } from './agent/rollback-handler.js';
|
|
5
|
+
export { signTelemetryPayload, verifyTelemetrySignature } from './agent/sign.js';
|
|
6
|
+
export { detectCampaignHaltedAlert, detectCriticalRiskAlert, detectSiteSilentAlert, dispatchAlert, raiseIfNew, } from './alerts/alerts.js';
|
|
7
|
+
export { createAlertConditionStore } from './alerts/conditions.js';
|
|
8
|
+
export { ALERT_TABLES, ensureAlertTables } from './alerts/tables.js';
|
|
9
|
+
export { createCommandQueueStore, FLEET_COMMAND_ACTIONS } from './control/commands.js';
|
|
10
|
+
export { generateControlPlaneIdentity } from './control/identity.js';
|
|
11
|
+
export { ingestTelemetry } from './control/ingest.js';
|
|
12
|
+
export { computeSiteRisk, filterRisks, groupRisksByClient, rankSitesByRisk, } from './control/risk.js';
|
|
13
|
+
export { createSiteStateStore } from './control/state.js';
|
|
14
|
+
export { CONTROL_TABLES, ensureControlTables } from './control/tables.js';
|
|
15
|
+
export { createEnrollmentStore } from './enrollment/store.js';
|
|
16
|
+
export { ensureFleetTables, FLEET_TABLES } from './enrollment/tables.js';
|
|
17
|
+
export { generatePairingToken, hashPairingToken } from './enrollment/tokens.js';
|
|
18
|
+
export { computeFleetBaseline, detectDrift, extractInventory } from './inventory/drift.js';
|
|
19
|
+
export { assembleClientReport, renderClientReport } from './reporting/report.js';
|
|
20
|
+
export { createReportScheduleStore, isReportDue } from './reporting/schedule.js';
|
|
21
|
+
export { ensureReportingTables, REPORTING_TABLES } from './reporting/tables.js';
|
|
22
|
+
export { createRolloutCampaignStore, orderSitesForCanary, planWaves, } from './rollout/campaign.js';
|
|
23
|
+
export { listRollbackCandidates, triggerRollback } from './rollout/rollback.js';
|
|
24
|
+
export { ensureRolloutTables, ROLLOUT_TABLES } from './rollout/tables.js';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAM1E,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAE9E,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AAEzE,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAYhF,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,aAAa,EACb,UAAU,GACX,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAOpE,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAEtF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAErD,OAAO,EACL,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAOzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAS/E,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE1F,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAEhF,OAAO,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAW/E,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,SAAS,GACV,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { TelemetrySnapshot } from '../control/state.js';
|
|
2
|
+
/**
|
|
3
|
+
* Kind of a version-bearing component a site reports. `cms` is its own kind
|
|
4
|
+
* (not a plugin/theme) even though today it is honestly `null` for every
|
|
5
|
+
* real site — no meaningful "Cogenta CMS version" exists anywhere in this
|
|
6
|
+
* codebase yet (pre-alpha; root `package.json` carries no `version` field,
|
|
7
|
+
* `@cogenta/plugins`' own `loadPlugin` documents the identical gap for
|
|
8
|
+
* `engineCompatible`). The `cms` component is included in the SHAPE so a
|
|
9
|
+
* later, real Cogenta version scheme slots in without a breaking change to
|
|
10
|
+
* this module's output — but a `null` `cms` version is never treated as
|
|
11
|
+
* "drifted" against anything (see `computeFleetBaseline`/`detectDrift`
|
|
12
|
+
* below: a component with no real version anywhere in the fleet has no
|
|
13
|
+
* baseline and produces no drift entries, honestly, rather than a fabricated
|
|
14
|
+
* "0.0.0 vs 0.0.0, no drift" result).
|
|
15
|
+
*/
|
|
16
|
+
export type InventoryComponentKind = 'cms' | 'plugin' | 'theme';
|
|
17
|
+
export interface InventoryComponent {
|
|
18
|
+
readonly kind: InventoryComponentKind;
|
|
19
|
+
/** The component's own name — `"cms"` for the kind `cms`, a plugin/theme's real name otherwise. */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly version: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SiteInventory {
|
|
24
|
+
readonly siteId: string;
|
|
25
|
+
/** When this inventory was collected — the site's own real `collectedAt`, not the ingestion time. */
|
|
26
|
+
readonly collectedAt: string;
|
|
27
|
+
readonly components: readonly InventoryComponent[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Real inventory extraction from a real, already-verified telemetry
|
|
31
|
+
* snapshot (`../control/state.js`'s `SiteStateStore`) — no new data source,
|
|
32
|
+
* just a flatter, component-oriented view of `TelemetryPayload.installedVersions`
|
|
33
|
+
* for the drift-comparison logic below to work over uniformly (cms/plugin/theme
|
|
34
|
+
* treated the same shape, rather than three separately-typed lists).
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractInventory(snapshot: TelemetrySnapshot): SiteInventory;
|
|
37
|
+
export interface FleetBaseline {
|
|
38
|
+
/** The expected/most-common version per real component key (`"plugin:my-plugin"`), computed from what the fleet actually reports — never hardcoded. */
|
|
39
|
+
readonly expectedVersion: ReadonlyMap<string, string>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* "Dérive" implies a baseline to drift FROM. The real, honestly-computable
|
|
43
|
+
* baseline today: the most common (mode) version of each component across
|
|
44
|
+
* the currently-known fleet. A tie is broken by the lexicographically
|
|
45
|
+
* greatest raw version string — an arbitrary but deterministic, real
|
|
46
|
+
* tie-break (not "first seen," which would depend on iteration order and
|
|
47
|
+
* make the baseline non-reproducible for the same inventory set).
|
|
48
|
+
*/
|
|
49
|
+
export declare function computeFleetBaseline(inventories: readonly SiteInventory[]): FleetBaseline;
|
|
50
|
+
export type DriftDirection = 'behind' | 'ahead' | 'different';
|
|
51
|
+
export interface DriftEntry {
|
|
52
|
+
readonly siteId: string;
|
|
53
|
+
readonly componentKind: InventoryComponentKind;
|
|
54
|
+
readonly componentName: string;
|
|
55
|
+
readonly version: string;
|
|
56
|
+
readonly expectedVersion: string;
|
|
57
|
+
/**
|
|
58
|
+
* `behind`/`ahead` when both the site's and the fleet's version parse as
|
|
59
|
+
* real semver (`@cogenta/plugins`'s real, already-tested comparator,
|
|
60
|
+
* reused rather than reimplemented) — `different` when they don't (a raw
|
|
61
|
+
* string mismatch is still real, reportable drift, it just can't be
|
|
62
|
+
* ordered without a parseable version on both sides).
|
|
63
|
+
*/
|
|
64
|
+
readonly direction: DriftDirection;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Compares every site's real inventory against the real fleet baseline and
|
|
68
|
+
* reports every component whose version differs — a site agreeing with the
|
|
69
|
+
* baseline on every component produces no entries for it, honestly (no
|
|
70
|
+
* "0 drift" placeholder row).
|
|
71
|
+
*/
|
|
72
|
+
export declare function detectDrift(inventories: readonly SiteInventory[], baseline: FleetBaseline): readonly DriftEntry[];
|
|
73
|
+
//# sourceMappingURL=drift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.d.ts","sourceRoot":"","sources":["../../src/inventory/drift.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE/D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAA;IACrC,mGAAmG;IACnG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,qGAAqG;IACrG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAA;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,aAAa,CAS3E;AAOD,MAAM,WAAW,aAAa;IAC5B,uJAAuJ;IACvJ,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,SAAS,aAAa,EAAE,GAAG,aAAa,CA6BzF;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAA;IAC9C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,SAAS,aAAa,EAAE,EACrC,QAAQ,EAAE,aAAa,GACtB,SAAS,UAAU,EAAE,CA8BvB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { compareVersions, parseVersion } from '@cogenta/plugins';
|
|
2
|
+
/**
|
|
3
|
+
* Real inventory extraction from a real, already-verified telemetry
|
|
4
|
+
* snapshot (`../control/state.js`'s `SiteStateStore`) — no new data source,
|
|
5
|
+
* just a flatter, component-oriented view of `TelemetryPayload.installedVersions`
|
|
6
|
+
* for the drift-comparison logic below to work over uniformly (cms/plugin/theme
|
|
7
|
+
* treated the same shape, rather than three separately-typed lists).
|
|
8
|
+
*/
|
|
9
|
+
export function extractInventory(snapshot) {
|
|
10
|
+
const { cms, plugins, themes } = snapshot.payload.installedVersions;
|
|
11
|
+
const components = [];
|
|
12
|
+
if (cms !== null)
|
|
13
|
+
components.push({ kind: 'cms', name: 'cms', version: cms });
|
|
14
|
+
for (const plugin of plugins)
|
|
15
|
+
components.push({ kind: 'plugin', name: plugin.name, version: plugin.version });
|
|
16
|
+
for (const theme of themes)
|
|
17
|
+
components.push({ kind: 'theme', name: theme.name, version: theme.version });
|
|
18
|
+
return { siteId: snapshot.siteId, collectedAt: snapshot.collectedAt, components };
|
|
19
|
+
}
|
|
20
|
+
/** Identifies one real component across the fleet, independent of which site reports it. */
|
|
21
|
+
function componentKey(kind, name) {
|
|
22
|
+
return `${kind}:${name}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* "Dérive" implies a baseline to drift FROM. The real, honestly-computable
|
|
26
|
+
* baseline today: the most common (mode) version of each component across
|
|
27
|
+
* the currently-known fleet. A tie is broken by the lexicographically
|
|
28
|
+
* greatest raw version string — an arbitrary but deterministic, real
|
|
29
|
+
* tie-break (not "first seen," which would depend on iteration order and
|
|
30
|
+
* make the baseline non-reproducible for the same inventory set).
|
|
31
|
+
*/
|
|
32
|
+
export function computeFleetBaseline(inventories) {
|
|
33
|
+
const counts = new Map();
|
|
34
|
+
for (const inventory of inventories) {
|
|
35
|
+
for (const component of inventory.components) {
|
|
36
|
+
const key = componentKey(component.kind, component.name);
|
|
37
|
+
const perVersion = counts.get(key) ?? new Map();
|
|
38
|
+
perVersion.set(component.version, (perVersion.get(component.version) ?? 0) + 1);
|
|
39
|
+
counts.set(key, perVersion);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const expectedVersion = new Map();
|
|
43
|
+
for (const [key, perVersion] of counts) {
|
|
44
|
+
let bestVersion;
|
|
45
|
+
let bestCount = -1;
|
|
46
|
+
for (const [version, count] of perVersion) {
|
|
47
|
+
if (count > bestCount ||
|
|
48
|
+
(count === bestCount && bestVersion !== undefined && version > bestVersion)) {
|
|
49
|
+
bestVersion = version;
|
|
50
|
+
bestCount = count;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (bestVersion !== undefined)
|
|
54
|
+
expectedVersion.set(key, bestVersion);
|
|
55
|
+
}
|
|
56
|
+
return { expectedVersion };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Compares every site's real inventory against the real fleet baseline and
|
|
60
|
+
* reports every component whose version differs — a site agreeing with the
|
|
61
|
+
* baseline on every component produces no entries for it, honestly (no
|
|
62
|
+
* "0 drift" placeholder row).
|
|
63
|
+
*/
|
|
64
|
+
export function detectDrift(inventories, baseline) {
|
|
65
|
+
const entries = [];
|
|
66
|
+
for (const inventory of inventories) {
|
|
67
|
+
for (const component of inventory.components) {
|
|
68
|
+
const key = componentKey(component.kind, component.name);
|
|
69
|
+
const expected = baseline.expectedVersion.get(key);
|
|
70
|
+
if (expected === undefined || expected === component.version)
|
|
71
|
+
continue;
|
|
72
|
+
const siteVersion = parseVersion(component.version);
|
|
73
|
+
const expectedParsed = parseVersion(expected);
|
|
74
|
+
const direction = siteVersion === null || expectedParsed === null
|
|
75
|
+
? 'different'
|
|
76
|
+
: compareVersions(siteVersion, expectedParsed) < 0
|
|
77
|
+
? 'behind'
|
|
78
|
+
: 'ahead';
|
|
79
|
+
entries.push({
|
|
80
|
+
siteId: inventory.siteId,
|
|
81
|
+
componentKind: component.kind,
|
|
82
|
+
componentName: component.name,
|
|
83
|
+
version: component.version,
|
|
84
|
+
expectedVersion: expected,
|
|
85
|
+
direction,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return entries;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/inventory/drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { CruxMetrics, Urgency } from '@cogenta/agents-builtin';
|
|
2
|
+
import type { ReportChannelMessage } from '@cogenta/channels';
|
|
3
|
+
import type { TelemetrySnapshot } from '../control/state.js';
|
|
4
|
+
import type { SiteRegistration } from '../enrollment/store.js';
|
|
5
|
+
import type { DriftEntry } from '../inventory/drift.js';
|
|
6
|
+
/**
|
|
7
|
+
* "## Rapports client" (`docs/lots/L8-flotte.md`): "Un rapport mensuel par
|
|
8
|
+
* site... C'est un livrable commercial pour l'agence, pas un tableau
|
|
9
|
+
* technique. Le format doit pouvoir être lu par le client final."
|
|
10
|
+
*
|
|
11
|
+
* A field the lot names but for which no real data source exists anywhere
|
|
12
|
+
* in this codebase yet (`available: false`) is never fabricated — the whole
|
|
13
|
+
* point of this shape is that a caller CANNOT accidentally read a plausible
|
|
14
|
+
* number out of an unavailable field, the same "structural honesty" `../agent/types.js`'s
|
|
15
|
+
* `TelemetryPayload` already applies to what a site reports upstream.
|
|
16
|
+
*/
|
|
17
|
+
export type ReportField<T> = {
|
|
18
|
+
readonly available: true;
|
|
19
|
+
readonly value: T;
|
|
20
|
+
} | {
|
|
21
|
+
readonly available: false;
|
|
22
|
+
};
|
|
23
|
+
export interface ClientReport {
|
|
24
|
+
readonly siteId: string;
|
|
25
|
+
readonly siteName: string;
|
|
26
|
+
readonly client: string | null;
|
|
27
|
+
readonly generatedAt: string;
|
|
28
|
+
/** Real when `../agent/types.js`'s `AvailabilitySummary.uptimeRatio` is non-null; `../agent/types.js` itself documents no real uptime monitor exists yet, so this is honestly `available: false` for every site today. */
|
|
29
|
+
readonly availability: ReportField<number>;
|
|
30
|
+
/** Real, always — `TelemetryPayload.openCves`, the security agent's real OSV/EPSS pipeline (tasks 2/5). Never `available: false`: an empty list is itself the real, reportable "no known incidents" answer, not an unavailable field. */
|
|
31
|
+
readonly securityIncidents: readonly {
|
|
32
|
+
readonly urgency: Urgency;
|
|
33
|
+
readonly status: 'open' | 'patched' | 'ignored';
|
|
34
|
+
}[];
|
|
35
|
+
/** Real when the performance agent's CrUX pipeline has field data for this site (`../agent/types.js`'s own `null` case: not enough real-user traffic, not an error); `available: false` only when no telemetry has ever been received at all. */
|
|
36
|
+
readonly performance: ReportField<CruxMetrics>;
|
|
37
|
+
/** No real "content published" count exists anywhere in `TelemetryPayload` or any other fleet-visible signal — the lot's own absolute "aucune donnée de contenu ne remonte" rule means this could only ever be a real operational COUNT, and no such count is collected today. Always `available: false`, honestly, never estimated. */
|
|
38
|
+
readonly publishedContent: ReportField<number>;
|
|
39
|
+
/** No live `AgentRegistry` exists anywhere in this codebase (R2-honest finding, repeated across L5/L7/L9/L8) — a site's own AI agents' actions have no real fleet-visible signal to report. Always `available: false`. */
|
|
40
|
+
readonly agentActions: ReportField<number>;
|
|
41
|
+
/** Real when `AvailabilitySummary`'s sibling `BackupSummary.lastResult` is not `'unknown'` — `../agent/types.js` documents no real backup mechanism exists yet, so this is honestly `available: false` for every site today. */
|
|
42
|
+
readonly backups: ReportField<{
|
|
43
|
+
readonly at: string;
|
|
44
|
+
readonly result: 'success' | 'failure';
|
|
45
|
+
}>;
|
|
46
|
+
/** Real — every component (`../inventory/drift.js`'s `DriftEntry`) this specific site is drifted on, already filtered to `siteId`. Empty is itself the real "up to date with the fleet" answer. */
|
|
47
|
+
readonly versionDrift: readonly DriftEntry[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Real assembly from real, already-verified fleet data (tasks 3/4/5's real
|
|
51
|
+
* stores) — no new data source, a commercial-report-shaped view over what
|
|
52
|
+
* already exists. `driftEntries` is the FULL fleet's drift list (as
|
|
53
|
+
* `../inventory/drift.js`'s `detectDrift` returns it) — filtered here to
|
|
54
|
+
* this one site, never a caller's job to pre-filter.
|
|
55
|
+
*/
|
|
56
|
+
export declare function assembleClientReport(site: SiteRegistration, snapshot: TelemetrySnapshot | null, driftEntries: readonly DriftEntry[], now?: () => number): ClientReport;
|
|
57
|
+
/**
|
|
58
|
+
* Plain-language French, never a raw technical identifier or a metric
|
|
59
|
+
* without a sentence around it — the literal acceptance criterion ("un
|
|
60
|
+
* rapport client est compréhensible par un non-technicien"), tested the
|
|
61
|
+
* same way L7's `describeCapability` proved it: no bare CVE id, no raw
|
|
62
|
+
* percentile number with nothing to say what it means.
|
|
63
|
+
*/
|
|
64
|
+
export declare function renderClientReport(report: ClientReport): ReportChannelMessage;
|
|
65
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/reporting/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IACrB;IAAE,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAA;CAAE,CAAA;AAQjC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,0NAA0N;IAC1N,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,yOAAyO;IACzO,QAAQ,CAAC,iBAAiB,EAAE,SAAS;QACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;QACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;KAChD,EAAE,CAAA;IACH,iPAAiP;IACjP,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;IAC9C,wUAAwU;IACxU,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC9C,0NAA0N;IAC1N,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,gOAAgO;IAChO,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;KAAE,CAAC,CAAA;IAC9F,mMAAmM;IACnM,QAAQ,CAAC,YAAY,EAAE,SAAS,UAAU,EAAE,CAAA;CAC7C;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,EAClC,YAAY,EAAE,SAAS,UAAU,EAAE,EACnC,GAAG,GAAE,MAAM,MAAiB,GAC3B,YAAY,CAiDd;AAeD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,oBAAoB,CAkE7E"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { buildReport } from '@cogenta/channels';
|
|
2
|
+
function available(value) {
|
|
3
|
+
return { available: true, value };
|
|
4
|
+
}
|
|
5
|
+
const unavailable = { available: false };
|
|
6
|
+
/**
|
|
7
|
+
* Real assembly from real, already-verified fleet data (tasks 3/4/5's real
|
|
8
|
+
* stores) — no new data source, a commercial-report-shaped view over what
|
|
9
|
+
* already exists. `driftEntries` is the FULL fleet's drift list (as
|
|
10
|
+
* `../inventory/drift.js`'s `detectDrift` returns it) — filtered here to
|
|
11
|
+
* this one site, never a caller's job to pre-filter.
|
|
12
|
+
*/
|
|
13
|
+
export function assembleClientReport(site, snapshot, driftEntries, now = Date.now) {
|
|
14
|
+
const siteDrift = driftEntries.filter((entry) => entry.siteId === site.id);
|
|
15
|
+
if (snapshot === null) {
|
|
16
|
+
return {
|
|
17
|
+
siteId: site.id,
|
|
18
|
+
siteName: site.name,
|
|
19
|
+
client: site.client,
|
|
20
|
+
generatedAt: new Date(now()).toISOString(),
|
|
21
|
+
availability: unavailable,
|
|
22
|
+
securityIncidents: [],
|
|
23
|
+
performance: unavailable,
|
|
24
|
+
publishedContent: unavailable,
|
|
25
|
+
agentActions: unavailable,
|
|
26
|
+
backups: unavailable,
|
|
27
|
+
versionDrift: siteDrift,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const { payload } = snapshot;
|
|
31
|
+
const availability = payload.availability.uptimeRatio === null
|
|
32
|
+
? unavailable
|
|
33
|
+
: available(payload.availability.uptimeRatio);
|
|
34
|
+
const performance = payload.coreWebVitalsAggregate === null
|
|
35
|
+
? unavailable
|
|
36
|
+
: available(payload.coreWebVitalsAggregate);
|
|
37
|
+
const backups = payload.backups.lastResult === 'unknown' || payload.backups.lastBackupAt === null
|
|
38
|
+
? unavailable
|
|
39
|
+
: available({ at: payload.backups.lastBackupAt, result: payload.backups.lastResult });
|
|
40
|
+
return {
|
|
41
|
+
siteId: site.id,
|
|
42
|
+
siteName: site.name,
|
|
43
|
+
client: site.client,
|
|
44
|
+
generatedAt: new Date(now()).toISOString(),
|
|
45
|
+
availability,
|
|
46
|
+
securityIncidents: payload.openCves.map((cve) => ({
|
|
47
|
+
urgency: cve.urgency,
|
|
48
|
+
status: cve.status,
|
|
49
|
+
})),
|
|
50
|
+
performance,
|
|
51
|
+
publishedContent: unavailable,
|
|
52
|
+
agentActions: unavailable,
|
|
53
|
+
backups,
|
|
54
|
+
versionDrift: siteDrift,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const URGENCY_FR = {
|
|
58
|
+
critical: 'critique',
|
|
59
|
+
high: 'élevée',
|
|
60
|
+
medium: 'moyenne',
|
|
61
|
+
low: 'faible',
|
|
62
|
+
};
|
|
63
|
+
const STATUS_FR = {
|
|
64
|
+
open: 'toujours ouverte',
|
|
65
|
+
patched: 'corrigée',
|
|
66
|
+
ignored: 'écartée après analyse',
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Plain-language French, never a raw technical identifier or a metric
|
|
70
|
+
* without a sentence around it — the literal acceptance criterion ("un
|
|
71
|
+
* rapport client est compréhensible par un non-technicien"), tested the
|
|
72
|
+
* same way L7's `describeCapability` proved it: no bare CVE id, no raw
|
|
73
|
+
* percentile number with nothing to say what it means.
|
|
74
|
+
*/
|
|
75
|
+
export function renderClientReport(report) {
|
|
76
|
+
const openCount = report.securityIncidents.filter((cve) => cve.status === 'open').length;
|
|
77
|
+
const criticalOpen = report.securityIncidents.some((cve) => cve.status === 'open' && cve.urgency === 'critical');
|
|
78
|
+
const keyFigures = [
|
|
79
|
+
{
|
|
80
|
+
label: 'Sécurité',
|
|
81
|
+
value: openCount === 0
|
|
82
|
+
? 'Aucune faille connue en attente'
|
|
83
|
+
: `${openCount} faille${openCount > 1 ? 's' : ''} de sécurité en attente de correction${criticalOpen ? ' (dont au moins une critique)' : ''}`,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: 'Mise à jour',
|
|
87
|
+
value: report.versionDrift.length === 0
|
|
88
|
+
? 'Le site est à jour avec le reste de la flotte'
|
|
89
|
+
: `${report.versionDrift.length} composant${report.versionDrift.length > 1 ? 's' : ''} à mettre à jour`,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
const sections = [];
|
|
93
|
+
sections.push({
|
|
94
|
+
heading: 'Sécurité',
|
|
95
|
+
body: report.securityIncidents.length === 0
|
|
96
|
+
? 'Aucun incident connu.'
|
|
97
|
+
: report.securityIncidents
|
|
98
|
+
.map((cve) => `Faille ${URGENCY_FR[cve.urgency]}, ${STATUS_FR[cve.status]}.`)
|
|
99
|
+
.join(' '),
|
|
100
|
+
});
|
|
101
|
+
sections.push({
|
|
102
|
+
heading: 'Performances',
|
|
103
|
+
body: report.performance.available
|
|
104
|
+
? describePerformance(report.performance.value)
|
|
105
|
+
: 'Pas encore assez de visites pour mesurer la rapidité.',
|
|
106
|
+
});
|
|
107
|
+
sections.push({
|
|
108
|
+
heading: 'Disponibilité',
|
|
109
|
+
body: report.availability.available
|
|
110
|
+
? `Répond ${Math.round(report.availability.value * 100)}% du temps.`
|
|
111
|
+
: 'Suivi pas encore activé.',
|
|
112
|
+
});
|
|
113
|
+
sections.push({
|
|
114
|
+
heading: 'Sauvegardes',
|
|
115
|
+
body: report.backups.available
|
|
116
|
+
? `Dernière sauvegarde le ${new Date(report.backups.value.at).toLocaleDateString('fr-FR')} — ${report.backups.value.result === 'success' ? 'réussie' : 'échouée'}.`
|
|
117
|
+
: 'Pas encore de suivi automatique.',
|
|
118
|
+
});
|
|
119
|
+
sections.push({
|
|
120
|
+
heading: 'Contenu et agents',
|
|
121
|
+
body: 'Pas encore disponible dans ce rapport.',
|
|
122
|
+
});
|
|
123
|
+
return buildReport({
|
|
124
|
+
title: `Rapport mensuel — ${report.siteName}`,
|
|
125
|
+
keyFigures,
|
|
126
|
+
sections,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function describePerformance(metrics) {
|
|
130
|
+
if (metrics.lcpP75Ms === undefined) {
|
|
131
|
+
return 'Des données de performance existent mais restent incomplètes pour ce site sur la période.';
|
|
132
|
+
}
|
|
133
|
+
const seconds = (metrics.lcpP75Ms / 1000).toFixed(1);
|
|
134
|
+
const feel = metrics.lcpP75Ms <= 2500 ? 'rapide' : metrics.lcpP75Ms <= 4000 ? 'correcte' : 'lente';
|
|
135
|
+
return `Le site met en moyenne ${seconds} seconde${Number(seconds) > 1 ? 's' : ''} à afficher son contenu principal pour un visiteur — une vitesse jugée ${feel}.`;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/reporting/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
export declare function isReportDue(lastSentAt: string | null, now?: () => number): boolean;
|
|
3
|
+
export interface ReportScheduleStore {
|
|
4
|
+
/** `null` if no report has ever been recorded as sent for this site. */
|
|
5
|
+
lastSentAt(siteId: string): Promise<string | null>;
|
|
6
|
+
/** Records that a report was sent for `siteId` at `now()` — real, idempotent (a later call simply overwrites the prior timestamp). */
|
|
7
|
+
recordSent(siteId: string, now?: () => number): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createReportScheduleStore(db: DatabaseHandle): ReportScheduleStore;
|
|
10
|
+
//# sourceMappingURL=schedule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/reporting/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,eAAe,CAAA;AAepE,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,OAAO,CAI5F;AAED,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAClD,sIAAsI;IACtI,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9D;AAOD,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,cAAc,GAAG,mBAAmB,CAmBjF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { identifier, sql } from '@cogenta/core';
|
|
2
|
+
import { REPORTING_TABLES } from './tables.js';
|
|
3
|
+
/**
|
|
4
|
+
* "Un rapport mensuel par site... planifiés" (`docs/lots/L8-flotte.md`).
|
|
5
|
+
* This is the real due/not-due computation only — no live cron/scheduler is
|
|
6
|
+
* wired anywhere in this codebase (the same honest "no live control-plane
|
|
7
|
+
* deployment exists" scoping every other L8 task has made); a real
|
|
8
|
+
* deployment calls `isReportDue` from whatever real scheduling mechanism it
|
|
9
|
+
* has, the same way `packages/create-cogenta/src/playground-reset.ts`
|
|
10
|
+
* (L9 task 12) is a real, callable function with no live cron wired to it
|
|
11
|
+
* either.
|
|
12
|
+
*/
|
|
13
|
+
const REPORT_INTERVAL_DAYS = 30;
|
|
14
|
+
export function isReportDue(lastSentAt, now = Date.now) {
|
|
15
|
+
if (lastSentAt === null)
|
|
16
|
+
return true;
|
|
17
|
+
const elapsedMs = now() - new Date(lastSentAt).getTime();
|
|
18
|
+
return elapsedMs >= REPORT_INTERVAL_DAYS * 24 * 60 * 60 * 1000;
|
|
19
|
+
}
|
|
20
|
+
export function createReportScheduleStore(db) {
|
|
21
|
+
const table = identifier(REPORTING_TABLES.schedule, db.dialect);
|
|
22
|
+
return {
|
|
23
|
+
async lastSentAt(siteId) {
|
|
24
|
+
const result = await db.query(sql `select site_id, last_sent_at from ${table} where site_id = ${siteId}`);
|
|
25
|
+
return result.rows[0]?.last_sent_at ?? null;
|
|
26
|
+
},
|
|
27
|
+
async recordSent(siteId, now = Date.now) {
|
|
28
|
+
const sentAt = new Date(now()).toISOString();
|
|
29
|
+
await db.query(sql `delete from ${table} where site_id = ${siteId}`);
|
|
30
|
+
await db.query(sql `insert into ${table} (site_id, last_sent_at) values (${siteId}, ${sentAt})`);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/reporting/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,UAAU,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9C;;;;;;;;;GASG;AACH,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B,MAAM,UAAU,WAAW,CAAC,UAAyB,EAAE,GAAG,GAAiB,IAAI,CAAC,GAAG;IACjF,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IACpC,MAAM,SAAS,GAAG,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAA;IACxD,OAAO,SAAS,IAAI,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAChE,CAAC;AAcD,MAAM,UAAU,yBAAyB,CAAC,EAAkB;IAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IAE/D,OAAO;QACL,KAAK,CAAC,UAAU,CAAC,MAAM;YACrB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,GAAG,CAAA,qCAAqC,KAAK,oBAAoB,MAAM,EAAE,CAC1E,CAAA;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,IAAI,CAAA;QAC7C,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG;YACrC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;YAC5C,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA,eAAe,KAAK,oBAAoB,MAAM,EAAE,CAAC,CAAA;YACnE,MAAM,EAAE,CAAC,KAAK,CACZ,GAAG,CAAA,eAAe,KAAK,oCAAoC,MAAM,KAAK,MAAM,GAAG,CAChF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
export declare const REPORTING_TABLES: {
|
|
3
|
+
readonly schedule: 'cogenta_fleet_report_schedule';
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Same `create table if not exists` pattern as `../control/tables.ts` — one
|
|
7
|
+
* row per site's last-sent report timestamp, strictly per-site (no query
|
|
8
|
+
* shape here spans more than one site's row at once).
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensureReportingTables(db: DatabaseHandle): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/reporting/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,gBAAgB;aAC3B,QAAQ,EAAE,+BAA+B;CACjC,CAAA;AAMV;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { identifier, sql, unsafeRaw, } from '@cogenta/core';
|
|
2
|
+
export const REPORTING_TABLES = {
|
|
3
|
+
schedule: 'cogenta_fleet_report_schedule',
|
|
4
|
+
};
|
|
5
|
+
function textColumn(dialect, length) {
|
|
6
|
+
return unsafeRaw(dialect === 'sqlite' ? 'text' : `varchar(${length})`);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Same `create table if not exists` pattern as `../control/tables.ts` — one
|
|
10
|
+
* row per site's last-sent report timestamp, strictly per-site (no query
|
|
11
|
+
* shape here spans more than one site's row at once).
|
|
12
|
+
*/
|
|
13
|
+
export async function ensureReportingTables(db) {
|
|
14
|
+
const t255 = textColumn(db.dialect, 255);
|
|
15
|
+
const schedule = identifier(REPORTING_TABLES.schedule, db.dialect);
|
|
16
|
+
await db.query(sql `
|
|
17
|
+
create table if not exists ${schedule} (
|
|
18
|
+
site_id ${t255} not null primary key,
|
|
19
|
+
last_sent_at ${t255} not null
|
|
20
|
+
)`);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../../src/reporting/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EAEV,GAAG,EACH,SAAS,GACV,MAAM,eAAe,CAAA;AAEtB,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,+BAA+B;CACjC,CAAA;AAEV,SAAS,UAAU,CAAC,OAAwB,EAAE,MAAc;IAC1D,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG,CAAC,CAAA;AACxE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,EAAkB;IAC5D,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;IACxC,MAAM,QAAQ,GAAG,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IAClE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iCACa,QAAQ;gBACzB,IAAI;qBACC,IAAI;MACnB,CAAC,CAAA;AACP,CAAC"}
|