@runfusion/fusion 0.25.0 → 0.27.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 +6 -0
- package/dist/bin.js +28004 -16888
- package/dist/client/assets/AgentDetailView-B7QRcHJH.css +1 -0
- package/dist/client/assets/AgentDetailView-DwLmRXTY.js +18 -0
- package/dist/client/assets/{AgentsView-B3jYk8Kt.js → AgentsView-D-N6aA0P.js} +12 -7
- package/dist/client/assets/ChatView-DnCdKu8Z.js +1 -0
- package/dist/client/assets/{DevServerView-DyGDEiBP.js → DevServerView-BiA1nYtt.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-D5UIeIl6.js → DirectoryPicker-DvBviDG6.js} +1 -1
- package/dist/client/assets/{DocumentsView-DNHu1T8K.js → DocumentsView-BWXOxpuq.js} +1 -1
- package/dist/client/assets/{EvalsView-CpRobtDi.js → EvalsView-CJFbtL7i.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-DOY_oZi7.js → ExperimentalAgentOnboardingModal-DuGIPd0B.js} +1 -1
- package/dist/client/assets/InsightsView-BBpRiolN.js +11 -0
- package/dist/client/assets/{MemoryView-PSc5lGJt.js → MemoryView-48LuNkKk.js} +2 -2
- package/dist/client/assets/NodesView-CGQWSNZM.js +14 -0
- package/dist/client/assets/{PiExtensionsManager-DL_QcN56.js → PiExtensionsManager-i-7UL2oh.js} +2 -2
- package/dist/client/assets/PluginManager-DoSAykD6.js +1 -0
- package/dist/client/assets/{ResearchView-BzCcDAS4.css → ResearchView-BEI4ZSGs.css} +1 -1
- package/dist/client/assets/ResearchView-XZuRtOxE.js +1 -0
- package/dist/client/assets/SettingsModal-Ci0_sqbU.css +1 -0
- package/dist/client/assets/{SettingsModal-CUCyaAyE.js → SettingsModal-CmeF8CN4.js} +1 -1
- package/dist/client/assets/SettingsModal-DBcjf9Bu.js +31 -0
- package/dist/client/assets/SettingsModal-DWKgRxBA.css +1 -0
- package/dist/client/assets/{SetupWizardModal-BKscasuh.js → SetupWizardModal-CgtvpMX9.js} +1 -1
- package/dist/client/assets/{SkillsView-BdELqTy7.js → SkillsView-DErYRumF.js} +1 -1
- package/dist/client/assets/StashRecoveryView-B_8WIQEo.css +1 -0
- package/dist/client/assets/StashRecoveryView-QJrNS4Vg.js +1 -0
- package/dist/client/assets/{TodoView-DFNGBDNV.js → TodoView-BD9NRwq0.js} +2 -2
- package/dist/client/assets/createLucideIcon-BazL2hk5.js +21 -0
- package/dist/client/assets/dashboard-view-BWGH_fAq.js +63 -0
- package/dist/client/assets/dashboard-view-BoTzlP8b.css +1 -0
- package/dist/client/assets/dashboard-view-DdGlfuu-.css +1 -0
- package/dist/client/assets/dashboard-view-Ws9_ZnKu.js +21 -0
- package/dist/client/assets/{folder-open-k1xmUMyr.js → folder-open-CHSlllzf.js} +1 -1
- package/dist/client/assets/index-DCovGm5b.css +1 -0
- package/dist/client/assets/index-bEwSVl7B.js +692 -0
- package/dist/client/assets/{star-ne32r3Y4.js → star-BgVwWAPz.js} +1 -1
- package/dist/client/assets/{upload-MS-2Gx53.js → upload-CAzycxr9.js} +1 -1
- package/dist/client/assets/{users-C519GSjH.js → users-CZnxCCCJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/droid-cli/src/__tests__/index.test.ts +228 -0
- package/dist/extension.js +15810 -10205
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/pi-claude-cli/src/__tests__/provider.test.ts +36 -22
- package/dist/pi-claude-cli/src/provider.ts +7 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/manifest.json +24 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +44 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/TestRunnerPanel.test.tsx +99 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/config-flow.test.ts +91 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-view.test.tsx +40 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-views.test.ts +46 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/draft-store.test.ts +50 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/exec-mock.ts +80 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/fixtures.test.ts +40 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/registry.ts +82 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/generator.test.ts +54 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manage-view.test.tsx +98 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manifest.test.ts +36 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/registration.test.ts +29 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/run-routes.test.ts +98 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runner.test.ts +55 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runtime-availability.test.ts +61 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/validation.test.ts +30 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/wizard-routes.test.ts +61 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/workflow-integration.test.ts +19 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.css +43 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.tsx +49 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/generator.ts +95 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/redact.ts +9 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/runner.ts +79 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/types.ts +31 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/index.ts +58 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/EditDraftModal.tsx +75 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/useDrafts.ts +73 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.css +79 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.tsx +122 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/routes/wizard-routes.ts +272 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.css +70 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.tsx +98 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/run/useRunGeneratedCli.ts +37 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/__tests__/executor-runtime-env.test.ts +191 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts +75 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/storage/draft-store.ts +85 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/cli-press-store.test.ts +128 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/credentials.test.ts +62 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts +427 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-types.ts +110 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/credentials.ts +95 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/steps.tsx +55 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/types.ts +33 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/validation.ts +63 -0
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/manifest.json +10 -0
- package/dist/plugins/fusion-plugin-reports/package.json +18 -2
- package/dist/plugins/fusion-plugin-reports/src/__tests__/approval.test.ts +164 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +14 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/routes-approval.test.ts +109 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/scaffold.test.ts +60 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/share-blocks.test.ts +83 -0
- package/dist/plugins/fusion-plugin-reports/src/aggregation.ts +23 -0
- package/dist/plugins/fusion-plugin-reports/src/approval.ts +97 -0
- package/dist/plugins/fusion-plugin-reports/src/cadence.ts +23 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.css +82 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.tsx +24 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportComparisonDrawer.test.tsx +12 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportDetailPanel.test.tsx +12 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportFiltersBar.test.tsx +14 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportsView.test.tsx +27 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/api.test.ts +19 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReportSectionDiff.test.ts +11 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReports.test.ts +13 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/api.ts +85 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.css +59 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.tsx +58 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportComparisonDrawer.tsx +21 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportDetailPanel.tsx +29 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportEmptyState.tsx +3 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportFiltersBar.tsx +19 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportListItem.tsx +8 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.css +29 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.tsx +43 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ReportApprovalPanel.test.tsx +38 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ShareBlocksPanel.test.tsx +24 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/test-setup.ts +18 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/types.ts +22 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportPreview.ts +44 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportSectionDiff.ts +59 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useReports.ts +71 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useViewportMode.ts +13 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard-view.tsx +6 -0
- package/dist/plugins/fusion-plugin-reports/src/index.ts +48 -2
- package/dist/plugins/fusion-plugin-reports/src/pipeline.ts +58 -0
- package/dist/plugins/fusion-plugin-reports/src/render/__tests__/escape.test.ts +20 -0
- package/dist/plugins/fusion-plugin-reports/src/render/__tests__/html-template.test.ts +110 -0
- package/dist/plugins/fusion-plugin-reports/src/render/__tests__/standalone-html.test.ts +66 -0
- package/dist/plugins/fusion-plugin-reports/src/render/escape.ts +12 -0
- package/dist/plugins/fusion-plugin-reports/src/render/html-styles.ts +40 -0
- package/dist/plugins/fusion-plugin-reports/src/render/html-template.ts +137 -0
- package/dist/plugins/fusion-plugin-reports/src/render/index.ts +4 -0
- package/dist/plugins/fusion-plugin-reports/src/render/standalone-html.ts +75 -0
- package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +31 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/__tests__/report-export-routes.test.ts +104 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/report-approval-routes.ts +98 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/report-export-routes.ts +77 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/report-list-routes.ts +72 -0
- package/dist/plugins/fusion-plugin-reports/src/runs-store.ts +69 -0
- package/dist/plugins/fusion-plugin-reports/src/share-blocks.ts +82 -0
- package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +51 -2
- package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +6 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +1672 -0
- package/dist/plugins/fusion-plugin-roadmap/manifest.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +4 -41
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +2 -3
- package/skill/fusion/references/engine-tools.md +1 -1
- package/skill/fusion/references/extension-tools.md +3 -3
- package/skill/fusion/references/fusion-capabilities.md +1 -1
- package/dist/client/assets/AgentDetailView-BwJaLqZh.css +0 -1
- package/dist/client/assets/AgentDetailView-ZbHEbYRT.js +0 -18
- package/dist/client/assets/ChatView-DhPkiEGs.js +0 -1
- package/dist/client/assets/InsightsView-vp0RE8Mg.js +0 -11
- package/dist/client/assets/NodesView-DMj6HGeC.js +0 -14
- package/dist/client/assets/PluginManager-BtYKm8IT.js +0 -1
- package/dist/client/assets/ResearchView-BhWqfdV0.js +0 -1
- package/dist/client/assets/SettingsModal-BAgB4_AR.js +0 -31
- package/dist/client/assets/SettingsModal-BNSrO1M9.css +0 -1
- package/dist/client/assets/SettingsModal-DzsLquBu.css +0 -1
- package/dist/client/assets/index-Qq2JOOWx.css +0 -1
- package/dist/client/assets/index-TFYXEVpn.js +0 -692
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/api-client.test.ts +0 -101
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/index.test.ts +0 -92
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-routes.test.ts +0 -48
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-suggestions.test.ts +0 -31
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.css +0 -1299
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx +0 -2559
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx +0 -1144
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/useRoadmaps.test.ts +0 -1756
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/api.ts +0 -70
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/test-setup.ts +0 -7
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/types.ts +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useConfirm.ts +0 -8
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useRoadmaps.ts +0 -1188
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useViewportMode.ts +0 -20
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard-view.tsx +0 -6
- package/dist/plugins/fusion-plugin-roadmap/src/index.ts +0 -74
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-routes.ts +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-schema.ts +0 -41
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.d.ts +0 -15
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.ts +0 -15
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts +0 -283
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js +0 -21
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.ts +0 -310
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts +0 -5
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js +0 -361
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts +0 -408
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts +0 -68
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js +0 -300
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.ts +0 -381
- package/dist/plugins/fusion-plugin-roadmap/src/server/index.d.ts +0 -3
- package/dist/plugins/fusion-plugin-roadmap/src/server/index.ts +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-handoff.test.ts +0 -445
- package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-ordering.test.ts +0 -334
- package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-store.test.ts +0 -1318
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-handoff.ts +0 -163
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts +0 -37
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js +0 -188
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.ts +0 -311
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts +0 -299
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js +0 -765
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js.map +0 -1
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.ts +0 -1001
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { PluginContext, PluginRouteDefinition, PluginRouteResponse } from "@fusion/core";
|
|
2
|
+
import { ReportStore } from "../store/report-store.js";
|
|
3
|
+
import { renderReportHtml } from "../render/html-template.js";
|
|
4
|
+
import { renderStandaloneReportHtml, slugifyReportFilename } from "../render/standalone-html.js";
|
|
5
|
+
|
|
6
|
+
interface RouteRequest {
|
|
7
|
+
params: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const reportStoreCache = new WeakMap<object, ReportStore>();
|
|
11
|
+
|
|
12
|
+
function getStore(ctx: PluginContext): ReportStore {
|
|
13
|
+
const taskStoreWithReports = ctx.taskStore as PluginContext["taskStore"] & { getReportStore?: () => ReportStore };
|
|
14
|
+
if (typeof taskStoreWithReports.getReportStore === "function") {
|
|
15
|
+
return taskStoreWithReports.getReportStore();
|
|
16
|
+
}
|
|
17
|
+
const key = ctx.taskStore as object;
|
|
18
|
+
const cached = reportStoreCache.get(key);
|
|
19
|
+
if (cached) return cached;
|
|
20
|
+
const store = new ReportStore(ctx.taskStore.getDatabase());
|
|
21
|
+
reportStoreCache.set(key, store);
|
|
22
|
+
return store;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function notFound(message: string): PluginRouteResponse {
|
|
26
|
+
return { status: 404, body: { error: message } };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function conflict(message: string): PluginRouteResponse {
|
|
30
|
+
return { status: 409, body: { error: message } };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createReportExportRoutes(): PluginRouteDefinition[] {
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
method: "GET",
|
|
37
|
+
path: "/reports/:id/export.html",
|
|
38
|
+
handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
|
|
39
|
+
const request = req as RouteRequest;
|
|
40
|
+
const id = request.params.id;
|
|
41
|
+
const store = getStore(ctx);
|
|
42
|
+
const record = store.getReport(id);
|
|
43
|
+
if (!record) return notFound(`Report ${id} not found`);
|
|
44
|
+
if (record.status === "generating") return conflict(`Report ${id} is not generated yet`);
|
|
45
|
+
const html = record.renderedHtml ?? renderStandaloneReportHtml(record);
|
|
46
|
+
if (!record.renderedHtml) {
|
|
47
|
+
store.setRenderedHtml(id, html);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
status: 200,
|
|
51
|
+
body: html,
|
|
52
|
+
contentType: "text/html; charset=utf-8",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Disposition": `attachment; filename="${slugifyReportFilename(record)}"`,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
method: "GET",
|
|
61
|
+
path: "/reports/:id/preview.html",
|
|
62
|
+
handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
|
|
63
|
+
const request = req as RouteRequest;
|
|
64
|
+
const id = request.params.id;
|
|
65
|
+
const store = getStore(ctx);
|
|
66
|
+
const record = store.getReport(id);
|
|
67
|
+
if (!record) return notFound(`Report ${id} not found`);
|
|
68
|
+
if (record.status === "generating") return conflict(`Report ${id} is not generated yet`);
|
|
69
|
+
return {
|
|
70
|
+
status: 200,
|
|
71
|
+
body: renderReportHtml(record, { includeChrome: false }),
|
|
72
|
+
contentType: "text/html; charset=utf-8",
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { PluginContext, PluginRouteDefinition, PluginRouteResponse } from "@fusion/core";
|
|
2
|
+
import { ReportStore } from "../store/report-store.js";
|
|
3
|
+
|
|
4
|
+
interface RouteRequest {
|
|
5
|
+
params: Record<string, string>;
|
|
6
|
+
query?: Record<string, string | string[] | undefined>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const reportStoreCache = new WeakMap<object, ReportStore>();
|
|
10
|
+
|
|
11
|
+
function getStore(ctx: PluginContext): ReportStore {
|
|
12
|
+
const key = ctx.taskStore as object;
|
|
13
|
+
const cached = reportStoreCache.get(key);
|
|
14
|
+
if (cached) return cached;
|
|
15
|
+
const store = new ReportStore(ctx.taskStore.getDatabase());
|
|
16
|
+
reportStoreCache.set(key, store);
|
|
17
|
+
return store;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function badRequest(message: string): PluginRouteResponse {
|
|
21
|
+
return { status: 400, body: { error: message } };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createReportListRoutes(): PluginRouteDefinition[] {
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
method: "GET",
|
|
28
|
+
path: "/reports",
|
|
29
|
+
handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
|
|
30
|
+
const request = req as RouteRequest;
|
|
31
|
+
const query = request.query ?? {};
|
|
32
|
+
const cadence = typeof query.cadence === "string" && query.cadence.length > 0 ? query.cadence : undefined;
|
|
33
|
+
const status = typeof query.status === "string" && query.status.length > 0 ? query.status : undefined;
|
|
34
|
+
const periodStartFrom = typeof query.from === "string" && query.from.length > 0 ? query.from : undefined;
|
|
35
|
+
const periodStartTo = typeof query.to === "string" && query.to.length > 0 ? query.to : undefined;
|
|
36
|
+
const q = typeof query.q === "string" && query.q.length > 0 ? query.q.toLowerCase() : undefined;
|
|
37
|
+
const agent = typeof query.agentId === "string" && query.agentId.length > 0 ? query.agentId.toLowerCase() : undefined;
|
|
38
|
+
|
|
39
|
+
const store = getStore(ctx);
|
|
40
|
+
const reports = store.listReports({
|
|
41
|
+
cadence: cadence as never,
|
|
42
|
+
status: status as never,
|
|
43
|
+
periodStartFrom,
|
|
44
|
+
periodStartTo,
|
|
45
|
+
orderBy: "periodStart",
|
|
46
|
+
orderDir: "desc",
|
|
47
|
+
limit: 500,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const filtered = reports.filter((report) => {
|
|
51
|
+
if (q && !report.title.toLowerCase().includes(q)) return false;
|
|
52
|
+
if (agent) {
|
|
53
|
+
const agentIds = ((report.metadata?.agentIds as string[] | undefined) ?? []).map((id) => id.toLowerCase());
|
|
54
|
+
if (!agentIds.some((id) => id.includes(agent))) return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
return { status: 200, body: { reports: filtered } };
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
method: "GET",
|
|
63
|
+
path: "/reports/:id",
|
|
64
|
+
handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
|
|
65
|
+
const request = req as RouteRequest;
|
|
66
|
+
const report = getStore(ctx).getReport(request.params.id);
|
|
67
|
+
if (!report) return { status: 404, body: { error: `Report ${request.params.id} not found` } };
|
|
68
|
+
return { status: 200, body: { report } };
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* INTERIM IN-MEMORY STORE — replaced by FN-3784's persistent storage.
|
|
3
|
+
* The `ReportRunRecord` shape is intended to be a strict subset of FN-3784's eventual schema.
|
|
4
|
+
*/
|
|
5
|
+
import type { ReportsCadence } from "./cadence.js";
|
|
6
|
+
|
|
7
|
+
export type ReportRunStatus = "queued" | "running" | "review" | "approved" | "published" | "failed";
|
|
8
|
+
|
|
9
|
+
export interface ReportRunRecord {
|
|
10
|
+
id: string;
|
|
11
|
+
cadence: ReportsCadence;
|
|
12
|
+
status: ReportRunStatus;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
reportId?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ReportsRunsStore {
|
|
20
|
+
create(record: ReportRunRecord): Promise<ReportRunRecord>;
|
|
21
|
+
update(
|
|
22
|
+
id: string,
|
|
23
|
+
patch: Partial<Omit<ReportRunRecord, "id" | "createdAt">>,
|
|
24
|
+
): Promise<ReportRunRecord | undefined>;
|
|
25
|
+
get(id: string): Promise<ReportRunRecord | undefined>;
|
|
26
|
+
list(limit?: number): Promise<ReportRunRecord[]>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cloneRecord(record: ReportRunRecord): ReportRunRecord {
|
|
30
|
+
return { ...record };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createInMemoryReportsRunsStore(seed: ReportRunRecord[] = []): ReportsRunsStore {
|
|
34
|
+
const records = new Map<string, ReportRunRecord>(seed.map((record) => [record.id, cloneRecord(record)]));
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
async create(record) {
|
|
38
|
+
const stored = cloneRecord(record);
|
|
39
|
+
records.set(stored.id, stored);
|
|
40
|
+
return cloneRecord(stored);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async update(id, patch) {
|
|
44
|
+
const current = records.get(id);
|
|
45
|
+
if (!current) return undefined;
|
|
46
|
+
|
|
47
|
+
const next: ReportRunRecord = {
|
|
48
|
+
...current,
|
|
49
|
+
...patch,
|
|
50
|
+
updatedAt: patch.updatedAt ?? new Date().toISOString(),
|
|
51
|
+
};
|
|
52
|
+
records.set(id, next);
|
|
53
|
+
return cloneRecord(next);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async get(id) {
|
|
57
|
+
const record = records.get(id);
|
|
58
|
+
return record ? cloneRecord(record) : undefined;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async list(limit = 50) {
|
|
62
|
+
const clampedLimit = Math.max(0, limit);
|
|
63
|
+
return Array.from(records.values())
|
|
64
|
+
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
65
|
+
.slice(0, clampedLimit)
|
|
66
|
+
.map((record) => cloneRecord(record));
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Report } from "./store/report-types.js";
|
|
2
|
+
|
|
3
|
+
const MAX_ITEMS_PER_SECTION = 5;
|
|
4
|
+
const MAX_BLOCK_LENGTH = 1500;
|
|
5
|
+
|
|
6
|
+
export interface ShareBlocks {
|
|
7
|
+
plainText: string;
|
|
8
|
+
markdown: string;
|
|
9
|
+
slack: string;
|
|
10
|
+
emailHtml: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function sliceWithEllipsis(items: string[]): string[] {
|
|
14
|
+
if (items.length <= MAX_ITEMS_PER_SECTION) return items;
|
|
15
|
+
return [...items.slice(0, MAX_ITEMS_PER_SECTION), "…"];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getSections(report: Report): Array<{ heading: string; items: string[] }> {
|
|
19
|
+
const review = report.combinedReview;
|
|
20
|
+
const sections: Array<{ heading: string; items: string[] }> = [];
|
|
21
|
+
const wins = review?.mergedHighlights ?? [];
|
|
22
|
+
const highlights = review?.mergedSuggestions ?? [];
|
|
23
|
+
const lowlights = review?.mergedLowlights ?? [];
|
|
24
|
+
if (wins.length > 0) sections.push({ heading: "Wins", items: sliceWithEllipsis(wins) });
|
|
25
|
+
if (highlights.length > 0) sections.push({ heading: "Highlights", items: sliceWithEllipsis(highlights) });
|
|
26
|
+
if (lowlights.length > 0) sections.push({ heading: "Lowlights", items: sliceWithEllipsis(lowlights) });
|
|
27
|
+
return sections;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function trimBlock(text: string): string {
|
|
31
|
+
return text.length <= MAX_BLOCK_LENGTH ? text : `${text.slice(0, MAX_BLOCK_LENGTH - 1)}…`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function escapeMarkdown(value: string): string {
|
|
35
|
+
return value.replace(/[\\`*_{}\[\]()#+\-.!|<>]/g, "\\$&");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function escapeHtml(value: string): string {
|
|
39
|
+
return value
|
|
40
|
+
.replaceAll("&", "&")
|
|
41
|
+
.replaceAll("<", "<")
|
|
42
|
+
.replaceAll(">", ">")
|
|
43
|
+
.replaceAll('"', """)
|
|
44
|
+
.replaceAll("'", "'");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Builds deterministic share-ready text blocks. */
|
|
48
|
+
export function buildShareBlocks(report: Report): ShareBlocks {
|
|
49
|
+
const sections = getSections(report);
|
|
50
|
+
const heading = `${report.title}\n${report.periodStart} → ${report.periodEnd}`;
|
|
51
|
+
const plainText = trimBlock([
|
|
52
|
+
heading,
|
|
53
|
+
...sections.map((section) => `${section.heading}:\n${section.items.map((item) => `- ${item}`).join("\n")}`),
|
|
54
|
+
].join("\n\n"));
|
|
55
|
+
|
|
56
|
+
const reportUrl = `/reports/${encodeURIComponent(report.id)}`;
|
|
57
|
+
const markdown = trimBlock([
|
|
58
|
+
`## ${escapeMarkdown(report.title)}`,
|
|
59
|
+
`Period: ${escapeMarkdown(report.periodStart)} → ${escapeMarkdown(report.periodEnd)}`,
|
|
60
|
+
...sections.map((section) => `### ${section.heading}\n${section.items.map((item) => `- ${escapeMarkdown(item)}`).join("\n")}`),
|
|
61
|
+
`[Open report](${reportUrl})`,
|
|
62
|
+
].join("\n\n"));
|
|
63
|
+
|
|
64
|
+
const slack = trimBlock([
|
|
65
|
+
`*${report.title}*`,
|
|
66
|
+
`${report.periodStart} → ${report.periodEnd}`,
|
|
67
|
+
...sections.map((section) => `*${section.heading}*\n${section.items.map((item) => `• ${item}`).join("\n")}`),
|
|
68
|
+
`<${reportUrl}|Open report>`,
|
|
69
|
+
].join("\n\n"));
|
|
70
|
+
|
|
71
|
+
// Note: emailHtml uses hardcoded hex/inline styles for email-client compatibility — design-token rule does not apply here.
|
|
72
|
+
const emailHtml = [
|
|
73
|
+
`<div style="font-family:Arial,sans-serif;color:#1f2328;line-height:1.5;">`,
|
|
74
|
+
`<h2 style="margin:0 0 12px;color:#5B8DEF;">${escapeHtml(report.title)}</h2>`,
|
|
75
|
+
`<p style="margin:0 0 12px;">Period: ${escapeHtml(report.periodStart)} → ${escapeHtml(report.periodEnd)}</p>`,
|
|
76
|
+
...sections.map((section) => `<h3 style="margin:12px 0 6px;">${escapeHtml(section.heading)}</h3><ul style="margin:0 0 12px;padding-left:20px;">${section.items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`),
|
|
77
|
+
`<p style="margin:0;"><a href="${escapeHtml(reportUrl)}" style="color:#5B8DEF;">Open report</a></p>`,
|
|
78
|
+
`</div>`,
|
|
79
|
+
].join("");
|
|
80
|
+
|
|
81
|
+
return { plainText, markdown, slack, emailHtml };
|
|
82
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import type { Database } from "@fusion/core";
|
|
4
|
+
import type { ApprovalDecision, ApprovalState } from "../approval.js";
|
|
4
5
|
import type { CombinedReview } from "../review-types.js";
|
|
5
6
|
import {
|
|
6
7
|
type Report,
|
|
@@ -27,8 +28,12 @@ interface ReportRow {
|
|
|
27
28
|
publishedAt: string | null;
|
|
28
29
|
archivedAt: string | null;
|
|
29
30
|
failureReason: string | null;
|
|
31
|
+
approval_state: ApprovalState;
|
|
32
|
+
approval_history: string;
|
|
30
33
|
draftMarkdown: string | null;
|
|
31
34
|
renderedHtmlPath: string | null;
|
|
35
|
+
rendered_html: string | null;
|
|
36
|
+
rendered_html_generated_at: string | null;
|
|
32
37
|
metadataJson: string;
|
|
33
38
|
combinedReviewJson: string | null;
|
|
34
39
|
createdAt: string;
|
|
@@ -74,8 +79,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
74
79
|
publishedAt: null,
|
|
75
80
|
archivedAt: null,
|
|
76
81
|
failureReason: null,
|
|
82
|
+
approvalState: "not_required",
|
|
83
|
+
approvalHistory: [],
|
|
77
84
|
draftMarkdown: input.draftMarkdown ?? null,
|
|
78
85
|
renderedHtmlPath: null,
|
|
86
|
+
renderedHtml: null,
|
|
87
|
+
renderedHtmlGeneratedAt: null,
|
|
79
88
|
metadata: input.metadata ?? {},
|
|
80
89
|
combinedReview: null,
|
|
81
90
|
createdAt: now,
|
|
@@ -88,12 +97,14 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
88
97
|
id, cadence, periodStart, periodEnd, title, status,
|
|
89
98
|
generationStartedAt, generationCompletedAt, reviewStartedAt, reviewCompletedAt,
|
|
90
99
|
approvedAt, approvedBy, publishedAt, archivedAt, failureReason,
|
|
91
|
-
|
|
100
|
+
approval_state, approval_history,
|
|
101
|
+
draftMarkdown, renderedHtmlPath, rendered_html, rendered_html_generated_at, metadataJson, combinedReviewJson, createdAt, updatedAt
|
|
92
102
|
) VALUES (
|
|
93
103
|
@id, @cadence, @periodStart, @periodEnd, @title, @status,
|
|
94
104
|
@generationStartedAt, @generationCompletedAt, @reviewStartedAt, @reviewCompletedAt,
|
|
95
105
|
@approvedAt, @approvedBy, @publishedAt, @archivedAt, @failureReason,
|
|
96
|
-
@
|
|
106
|
+
@approvalState, @approvalHistory,
|
|
107
|
+
@draftMarkdown, @renderedHtmlPath, @renderedHtml, @renderedHtmlGeneratedAt, @metadataJson, @combinedReviewJson, @createdAt, @updatedAt
|
|
97
108
|
)
|
|
98
109
|
`).run(this.toDbParams(report, true));
|
|
99
110
|
});
|
|
@@ -157,7 +168,16 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
157
168
|
draftMarkdown: patch.draftMarkdown ?? current.draftMarkdown,
|
|
158
169
|
renderedHtmlPath: patch.renderedHtmlPath ?? current.renderedHtmlPath,
|
|
159
170
|
metadata: patch.metadata ?? current.metadata,
|
|
171
|
+
renderedHtml: patch.renderedHtml ?? current.renderedHtml,
|
|
172
|
+
renderedHtmlGeneratedAt: patch.renderedHtmlGeneratedAt ?? current.renderedHtmlGeneratedAt,
|
|
160
173
|
failureReason: patch.failureReason ?? current.failureReason,
|
|
174
|
+
approvalState: patch.approvalState ?? current.approvalState,
|
|
175
|
+
approvalHistory: patch.approvalHistory ?? current.approvalHistory,
|
|
176
|
+
status: patch.status ?? current.status,
|
|
177
|
+
approvedAt: patch.approvedAt ?? current.approvedAt,
|
|
178
|
+
approvedBy: patch.approvedBy ?? current.approvedBy,
|
|
179
|
+
publishedAt: patch.publishedAt ?? current.publishedAt,
|
|
180
|
+
reviewCompletedAt: patch.reviewCompletedAt ?? current.reviewCompletedAt,
|
|
161
181
|
updatedAt: new Date().toISOString(),
|
|
162
182
|
};
|
|
163
183
|
|
|
@@ -224,6 +244,13 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
224
244
|
return this.updateReport(id, { renderedHtmlPath: htmlPath });
|
|
225
245
|
}
|
|
226
246
|
|
|
247
|
+
setRenderedHtml(id: string, html: string): Report {
|
|
248
|
+
return this.updateReport(id, {
|
|
249
|
+
renderedHtml: html,
|
|
250
|
+
renderedHtmlGeneratedAt: new Date().toISOString(),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
227
254
|
deleteReport(id: string): void {
|
|
228
255
|
this.requireReport(id);
|
|
229
256
|
this.db.transaction(() => {
|
|
@@ -256,8 +283,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
256
283
|
publishedAt: row.publishedAt,
|
|
257
284
|
archivedAt: row.archivedAt,
|
|
258
285
|
failureReason: row.failureReason,
|
|
286
|
+
approvalState: row.approval_state,
|
|
287
|
+
approvalHistory: this.parseApprovalHistory(row.approval_history),
|
|
259
288
|
draftMarkdown: row.draftMarkdown,
|
|
260
289
|
renderedHtmlPath: row.renderedHtmlPath,
|
|
290
|
+
renderedHtml: row.rendered_html,
|
|
291
|
+
renderedHtmlGeneratedAt: row.rendered_html_generated_at,
|
|
261
292
|
metadata: this.parseMetadata(row.metadataJson),
|
|
262
293
|
combinedReview: this.parseCombinedReview(row.combinedReviewJson),
|
|
263
294
|
createdAt: row.createdAt,
|
|
@@ -282,8 +313,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
282
313
|
publishedAt = @publishedAt,
|
|
283
314
|
archivedAt = @archivedAt,
|
|
284
315
|
failureReason = @failureReason,
|
|
316
|
+
approval_state = @approvalState,
|
|
317
|
+
approval_history = @approvalHistory,
|
|
285
318
|
draftMarkdown = @draftMarkdown,
|
|
286
319
|
renderedHtmlPath = @renderedHtmlPath,
|
|
320
|
+
rendered_html = @renderedHtml,
|
|
321
|
+
rendered_html_generated_at = @renderedHtmlGeneratedAt,
|
|
287
322
|
metadataJson = @metadataJson,
|
|
288
323
|
combinedReviewJson = @combinedReviewJson,
|
|
289
324
|
updatedAt = @updatedAt
|
|
@@ -312,8 +347,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
312
347
|
publishedAt: report.publishedAt,
|
|
313
348
|
archivedAt: report.archivedAt,
|
|
314
349
|
failureReason: report.failureReason,
|
|
350
|
+
approvalState: report.approvalState,
|
|
351
|
+
approvalHistory: JSON.stringify(report.approvalHistory ?? []),
|
|
315
352
|
draftMarkdown: report.draftMarkdown,
|
|
316
353
|
renderedHtmlPath: report.renderedHtmlPath,
|
|
354
|
+
renderedHtml: report.renderedHtml,
|
|
355
|
+
renderedHtmlGeneratedAt: report.renderedHtmlGeneratedAt,
|
|
317
356
|
metadataJson: JSON.stringify(report.metadata ?? {}),
|
|
318
357
|
combinedReviewJson: report.combinedReview ? JSON.stringify(report.combinedReview) : null,
|
|
319
358
|
...(includeCreatedAt ? { createdAt: report.createdAt } : {}),
|
|
@@ -338,4 +377,14 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
|
|
|
338
377
|
return null;
|
|
339
378
|
}
|
|
340
379
|
}
|
|
380
|
+
|
|
381
|
+
private parseApprovalHistory(json: string | null): ApprovalDecision[] {
|
|
382
|
+
if (!json) return [];
|
|
383
|
+
try {
|
|
384
|
+
const parsed = JSON.parse(json);
|
|
385
|
+
return Array.isArray(parsed) ? parsed as ApprovalDecision[] : [];
|
|
386
|
+
} catch {
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
341
390
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ApprovalDecision, ApprovalState } from "../approval.js";
|
|
1
2
|
import type { CombinedReview } from "../review-types.js";
|
|
2
3
|
|
|
3
4
|
export type ReportCadence = "daily" | "weekly" | "monthly" | "quarterly" | "manual";
|
|
@@ -28,8 +29,12 @@ export interface Report {
|
|
|
28
29
|
publishedAt: string | null;
|
|
29
30
|
archivedAt: string | null;
|
|
30
31
|
failureReason: string | null;
|
|
32
|
+
approvalState: ApprovalState;
|
|
33
|
+
approvalHistory: ApprovalDecision[];
|
|
31
34
|
draftMarkdown: string | null;
|
|
32
35
|
renderedHtmlPath: string | null;
|
|
36
|
+
renderedHtml: string | null;
|
|
37
|
+
renderedHtmlGeneratedAt: string | null;
|
|
33
38
|
metadata: Record<string, unknown>;
|
|
34
39
|
combinedReview: CombinedReview | null;
|
|
35
40
|
createdAt: string;
|
|
@@ -45,7 +50,7 @@ export interface ReportCreateInput {
|
|
|
45
50
|
draftMarkdown?: string;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
export type ReportUpdateInput = Partial<Pick<Report, "title" | "draftMarkdown" | "renderedHtmlPath" | "metadata" | "failureReason">>;
|
|
53
|
+
export type ReportUpdateInput = Partial<Pick<Report, "title" | "draftMarkdown" | "renderedHtmlPath" | "renderedHtml" | "renderedHtmlGeneratedAt" | "metadata" | "failureReason" | "approvalState" | "approvalHistory" | "status" | "approvedAt" | "approvedBy" | "publishedAt" | "reviewCompletedAt">>;
|
|
49
54
|
|
|
50
55
|
export interface ReportListFilter {
|
|
51
56
|
cadence?: ReportCadence;
|