@runfusion/fusion 0.26.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/dist/bin.js +11036 -1992
- package/dist/client/assets/AgentDetailView-B7QRcHJH.css +1 -0
- package/dist/client/assets/AgentDetailView-DwLmRXTY.js +18 -0
- package/dist/client/assets/{AgentsView-D6Zi5zfP.js → AgentsView-D-N6aA0P.js} +12 -7
- package/dist/client/assets/ChatView-DnCdKu8Z.js +1 -0
- package/dist/client/assets/{DevServerView--_WBvIDQ.js → DevServerView-BiA1nYtt.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-xedtR-Rd.js → DirectoryPicker-DvBviDG6.js} +1 -1
- package/dist/client/assets/{DocumentsView-Bg2oaZks.js → DocumentsView-BWXOxpuq.js} +1 -1
- package/dist/client/assets/{EvalsView-B3uOCXfr.js → EvalsView-CJFbtL7i.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-Bx6yXVS5.js → ExperimentalAgentOnboardingModal-DuGIPd0B.js} +1 -1
- package/dist/client/assets/InsightsView-BBpRiolN.js +11 -0
- package/dist/client/assets/{MemoryView-xcN_eouf.js → MemoryView-48LuNkKk.js} +2 -2
- package/dist/client/assets/NodesView-CGQWSNZM.js +14 -0
- package/dist/client/assets/{PiExtensionsManager-Cc8aAZXg.js → PiExtensionsManager-i-7UL2oh.js} +2 -2
- package/dist/client/assets/PluginManager-DoSAykD6.js +1 -0
- package/dist/client/assets/{ResearchView-CERNf7sJ.js → ResearchView-XZuRtOxE.js} +1 -1
- package/dist/client/assets/{SettingsModal-Cis-4Lot.css → SettingsModal-Ci0_sqbU.css} +1 -1
- package/dist/client/assets/{SettingsModal-B1r0yASu.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-D1q548_L.js → SetupWizardModal-CgtvpMX9.js} +1 -1
- package/dist/client/assets/{SkillsView-ClLM6u6p.js → SkillsView-DErYRumF.js} +1 -1
- package/dist/client/assets/{StashRecoveryView-ze0pEZ5U.js → StashRecoveryView-QJrNS4Vg.js} +1 -1
- package/dist/client/assets/{TodoView-CTmIfy2M.js → TodoView-BD9NRwq0.js} +2 -2
- package/dist/client/assets/{dashboard-view-CyWN-d02.js → dashboard-view-BWGH_fAq.js} +1 -1
- package/dist/client/assets/dashboard-view-BoTzlP8b.css +1 -0
- package/dist/client/assets/dashboard-view-Ws9_ZnKu.js +21 -0
- package/dist/client/assets/{folder-open-BZuKESeq.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-D75YKEq-.js → star-BgVwWAPz.js} +1 -1
- package/dist/client/assets/{upload-BYYTgWFj.js → upload-CAzycxr9.js} +1 -1
- package/dist/client/assets/{users-RS90Aii3.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 +5517 -1193
- 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 +19 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +20 -2
- 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 +21 -5
- 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 +46 -2
- 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 +1528 -29391
- package/dist/plugins/fusion-plugin-roadmap/manifest.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- 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-Cv-vgOj3.js +0 -18
- package/dist/client/assets/ChatView-CAHjY9uO.js +0 -1
- package/dist/client/assets/InsightsView-Q1zvtF4F.js +0 -11
- package/dist/client/assets/NodesView-RxXg58_Q.js +0 -14
- package/dist/client/assets/PluginManager-BEkyBajl.js +0 -1
- package/dist/client/assets/SettingsModal-BLsac7CJ.js +0 -31
- package/dist/client/assets/SettingsModal-BNSrO1M9.css +0 -1
- package/dist/client/assets/dashboard-view-4xAN3yO5.js +0 -21
- package/dist/client/assets/dashboard-view-BkTMSZYn.css +0 -1
- package/dist/client/assets/index-Bdw6llW6.js +0 -692
- package/dist/client/assets/index-CZGlyJuS.css +0 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.css +0 -1093
|
@@ -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;
|