@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,43 @@
|
|
|
1
|
+
.cli-press-wizard {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-lg);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.cli-press-stepper {
|
|
8
|
+
display: flex;
|
|
9
|
+
gap: var(--space-sm);
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.cli-press-step {
|
|
14
|
+
border: 1px solid var(--border);
|
|
15
|
+
border-radius: var(--radius-pill);
|
|
16
|
+
padding: var(--space-xs) var(--space-md);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.cli-press-step.is-active {
|
|
20
|
+
background: var(--todo);
|
|
21
|
+
color: var(--accent-text);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.cli-press-endpoint-row {
|
|
25
|
+
display: grid;
|
|
26
|
+
gap: var(--space-sm);
|
|
27
|
+
grid-template-columns: 1fr 1fr 1fr auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.cli-press-actions {
|
|
31
|
+
display: flex;
|
|
32
|
+
gap: var(--space-sm);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (max-width: 768px) {
|
|
36
|
+
.cli-press-endpoint-row {
|
|
37
|
+
grid-template-columns: 1fr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.cli-press-actions {
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { PluginDashboardViewContext } from "@fusion/dashboard/app/plugins/types";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { BasicsStep, CredentialsStep, EndpointsStep, ReviewStep, TransportStep } from "./wizard/steps.js";
|
|
4
|
+
import type { ServiceDraft, WizardStep } from "./wizard/types.js";
|
|
5
|
+
import { validateBasics, validateCredentials, validateEndpoints, validateTransport } from "./wizard/validation.js";
|
|
6
|
+
import "./dashboard-view.css";
|
|
7
|
+
|
|
8
|
+
const STEPS: WizardStep[] = ["basics", "transport", "endpoints", "credentials", "review"];
|
|
9
|
+
|
|
10
|
+
function createInitialDraft(): ServiceDraft {
|
|
11
|
+
const now = new Date().toISOString();
|
|
12
|
+
return { id: "", name: "", slug: "", description: "", baseUrl: "", transport: "http", endpoints: [{ id: crypto.randomUUID(), name: "", method: "GET", path: "" }], credential: { kind: "none" }, createdAt: now, updatedAt: now };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function CliPrintingPressWizardView({ context: _context }: { context?: PluginDashboardViewContext }) {
|
|
16
|
+
const [draft, setDraft] = useState<ServiceDraft>(() => createInitialDraft());
|
|
17
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
18
|
+
const [savedId, setSavedId] = useState<string | null>(null);
|
|
19
|
+
const [error, setError] = useState<string | null>(null);
|
|
20
|
+
|
|
21
|
+
const currentStep = STEPS[stepIndex];
|
|
22
|
+
const currentValidation = useMemo(() => {
|
|
23
|
+
if (currentStep === "basics") return validateBasics(draft);
|
|
24
|
+
if (currentStep === "transport") return validateTransport(draft);
|
|
25
|
+
if (currentStep === "endpoints") return validateEndpoints(draft);
|
|
26
|
+
if (currentStep === "credentials") return validateCredentials(draft.credential);
|
|
27
|
+
return { ok: true } as const;
|
|
28
|
+
}, [currentStep, draft]);
|
|
29
|
+
|
|
30
|
+
async function onSave() {
|
|
31
|
+
const response = await fetch("/api/plugins/fusion-plugin-cli-printing-press/drafts", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(draft) });
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
const body = await response.json().catch(() => ({} as { error?: string; errors?: Record<string, string> }));
|
|
34
|
+
const firstError = body?.errors ? Object.values(body.errors)[0] : undefined;
|
|
35
|
+
setError(body?.error ?? firstError ?? "Failed to save draft");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const body = await response.json();
|
|
39
|
+
setSavedId(body.id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (savedId) {
|
|
43
|
+
return <section className="card"><h2>Saved — draft id {savedId}</h2><p>List, edit, regenerate, run/test, and runtime exposure land in FN-3764 / FN-3765 / FN-3767.</p></section>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <section className="cli-press-wizard"><div className="cli-press-stepper">{STEPS.map((step, index) => <span className={`cli-press-step${index === stepIndex ? " is-active" : ""}`} key={step}>{step}</span>)}</div>{error ? <p className="form-error">{error}</p> : null}{currentStep === "basics" ? <BasicsStep draft={draft} onChange={(patch) => setDraft((current) => ({ ...current, ...patch, updatedAt: new Date().toISOString() }))} /> : null}{currentStep === "transport" ? <TransportStep /> : null}{currentStep === "endpoints" ? <EndpointsStep draft={draft} onChange={(endpoints) => setDraft((current) => ({ ...current, endpoints, updatedAt: new Date().toISOString() }))} /> : null}{currentStep === "credentials" ? <CredentialsStep draft={draft} onChange={(credential) => setDraft((current) => ({ ...current, credential, updatedAt: new Date().toISOString() }))} /> : null}{currentStep === "review" ? <ReviewStep draft={draft} /> : null}<div className="cli-press-actions"><button className="btn" onClick={() => setDraft(createInitialDraft())}>Cancel</button><button className="btn" disabled={stepIndex === 0} onClick={() => setStepIndex((value) => Math.max(0, value - 1))}>Back</button>{stepIndex < STEPS.length - 1 ? <button className="btn btn-primary" disabled={!currentValidation.ok} onClick={() => setStepIndex((value) => Math.min(STEPS.length - 1, value + 1))}>Next</button> : <button className="btn btn-primary" onClick={() => void onSave()}>Save draft</button>}</div></section>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default CliPrintingPressWizardView;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { chmod, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { GenerateCliInput, GeneratedCliArtifact } from "./types.js";
|
|
4
|
+
|
|
5
|
+
function buildScript(draftJson: string): string {
|
|
6
|
+
return `#!/usr/bin/env node
|
|
7
|
+
const draft = ${draftJson};
|
|
8
|
+
|
|
9
|
+
function parseArgs(argv) {
|
|
10
|
+
const parsed = { endpoint: "", params: {} };
|
|
11
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
12
|
+
const part = argv[i];
|
|
13
|
+
if (part === "--endpoint") {
|
|
14
|
+
parsed.endpoint = String(argv[i + 1] ?? "");
|
|
15
|
+
i += 1;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (part.startsWith("--")) {
|
|
19
|
+
const key = part.slice(2);
|
|
20
|
+
const next = argv[i + 1];
|
|
21
|
+
if (next === undefined || String(next).startsWith("--")) {
|
|
22
|
+
parsed.params[key] = true;
|
|
23
|
+
} else {
|
|
24
|
+
parsed.params[key] = String(next);
|
|
25
|
+
i += 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function endpointUrl(baseUrl, path) {
|
|
33
|
+
return new URL(path, baseUrl).toString();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
(async () => {
|
|
37
|
+
const args = parseArgs(process.argv.slice(2));
|
|
38
|
+
const endpoint = draft.endpoints.find((item) => item.id === args.endpoint);
|
|
39
|
+
if (!endpoint) {
|
|
40
|
+
process.stderr.write(\`Unknown endpoint: \${args.endpoint}\\n\`);
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const method = endpoint.method || "GET";
|
|
45
|
+
const url = endpointUrl(draft.baseUrl, endpoint.path || "/");
|
|
46
|
+
const headers = { "content-type": "application/json" };
|
|
47
|
+
|
|
48
|
+
if (draft.credential?.kind === "apiKey") {
|
|
49
|
+
const envValue = process.env[draft.credential.envVar] ?? process.env[\`CLIPP_CRED_\${String(draft.credential.envVar).toUpperCase()}\`];
|
|
50
|
+
if (envValue) headers[draft.credential.header] = envValue;
|
|
51
|
+
}
|
|
52
|
+
if (draft.credential?.kind === "bearerToken") {
|
|
53
|
+
const token = process.env[draft.credential.envVar] ?? process.env[\`CLIPP_CRED_\${String(draft.credential.envVar).toUpperCase()}\`];
|
|
54
|
+
if (token) headers.authorization = \`Bearer \${token}\`;
|
|
55
|
+
}
|
|
56
|
+
if (draft.credential?.kind === "basicAuth") {
|
|
57
|
+
const username = process.env[draft.credential.usernameEnvVar] ?? process.env[\`CLIPP_CRED_\${String(draft.credential.usernameEnvVar).toUpperCase()}\`];
|
|
58
|
+
const password = process.env[draft.credential.passwordEnvVar] ?? process.env[\`CLIPP_CRED_\${String(draft.credential.passwordEnvVar).toUpperCase()}\`];
|
|
59
|
+
if (username || password) {
|
|
60
|
+
headers.authorization = "Basic " + Buffer.from(String(username ?? "") + ":" + String(password ?? "")).toString("base64");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const body = method === "GET" || method === "DELETE" ? undefined : JSON.stringify(args.params);
|
|
65
|
+
const response = await fetch(url, { method, headers, body });
|
|
66
|
+
const text = await response.text();
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
process.stderr.write(text || \`HTTP \${response.status}\\n\`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write(text);
|
|
72
|
+
})().catch((error) => {
|
|
73
|
+
process.stderr.write(String(error?.message ?? error));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function generateCli({ draft, outDir }: GenerateCliInput): Promise<GeneratedCliArtifact> {
|
|
80
|
+
await mkdir(outDir, { recursive: true });
|
|
81
|
+
const binPath = join(outDir, `${draft.slug}.mjs`);
|
|
82
|
+
await writeFile(binPath, buildScript(JSON.stringify(draft)), "utf8");
|
|
83
|
+
|
|
84
|
+
if (process.platform !== "win32") {
|
|
85
|
+
await chmod(binPath, 0o755);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
draftId: draft.id,
|
|
90
|
+
slug: draft.slug,
|
|
91
|
+
binPath,
|
|
92
|
+
entrypoint: "node",
|
|
93
|
+
generatedAt: new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function escapeRegExp(value: string): string {
|
|
2
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function redact(text: string, secrets: string[]): string {
|
|
6
|
+
if (!text) return text;
|
|
7
|
+
const unique = [...new Set(secrets.filter((secret) => secret && secret.length > 0))].sort((a, b) => b.length - a.length);
|
|
8
|
+
return unique.reduce((acc, secret) => acc.replace(new RegExp(escapeRegExp(secret), "g"), "***"), text);
|
|
9
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { redact } from "./redact.js";
|
|
4
|
+
import type { GeneratedCliArtifact, RunResult } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
|
|
8
|
+
export interface RunGeneratedCliInput {
|
|
9
|
+
artifact: GeneratedCliArtifact;
|
|
10
|
+
endpointId: string;
|
|
11
|
+
params: Record<string, string | number | boolean>;
|
|
12
|
+
credentials?: Record<string, string>;
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function toFlagName(key: string): string {
|
|
18
|
+
return key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createArgs(endpointId: string, params: Record<string, string | number | boolean>): string[] {
|
|
22
|
+
const args: string[] = ["--endpoint", endpointId];
|
|
23
|
+
for (const [key, value] of Object.entries(params)) {
|
|
24
|
+
if (typeof value === "boolean") {
|
|
25
|
+
if (value) args.push(`--${toFlagName(key)}`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
args.push(`--${toFlagName(key)}`, String(value));
|
|
29
|
+
}
|
|
30
|
+
return args;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function quoteArg(arg: string): string {
|
|
34
|
+
return JSON.stringify(arg);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Credentials are passed only via env vars: CLIPP_CRED_<UPPER_SNAKE_KEY>.
|
|
38
|
+
export async function runGeneratedCli({ artifact, endpointId, params, credentials, timeoutMs = 30_000, cwd }: RunGeneratedCliInput): Promise<RunResult> {
|
|
39
|
+
const args = createArgs(endpointId, params);
|
|
40
|
+
const argv = [artifact.binPath, ...args];
|
|
41
|
+
const command = ["node", ...argv].map(quoteArg).join(" ");
|
|
42
|
+
|
|
43
|
+
const credEnv: Record<string, string> = {};
|
|
44
|
+
for (const [key, value] of Object.entries(credentials ?? {})) {
|
|
45
|
+
credEnv[`CLIPP_CRED_${key.replace(/[^a-zA-Z0-9]/g, "_").toUpperCase()}`] = value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const start = Date.now();
|
|
49
|
+
try {
|
|
50
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
51
|
+
cwd,
|
|
52
|
+
timeout: timeoutMs,
|
|
53
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
54
|
+
env: { ...process.env, ...credEnv },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const secrets = Object.values(credentials ?? {});
|
|
58
|
+
return {
|
|
59
|
+
stdout: redact(stdout, secrets),
|
|
60
|
+
stderr: redact(stderr, secrets),
|
|
61
|
+
exitCode: 0,
|
|
62
|
+
durationMs: Date.now() - start,
|
|
63
|
+
timedOut: false,
|
|
64
|
+
argv: argv.map((part) => redact(part, secrets)),
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
const err = error as { stdout?: string; stderr?: string; code?: number | null; killed?: boolean; signal?: string };
|
|
68
|
+
const timedOut = Boolean(err.killed && err.signal === "SIGTERM");
|
|
69
|
+
const secrets = Object.values(credentials ?? {});
|
|
70
|
+
return {
|
|
71
|
+
stdout: redact(err.stdout ?? "", secrets),
|
|
72
|
+
stderr: redact(err.stderr ?? (timedOut ? "Command timed out" : ""), secrets),
|
|
73
|
+
exitCode: timedOut ? null : (typeof err.code === "number" ? err.code : null),
|
|
74
|
+
durationMs: Date.now() - start,
|
|
75
|
+
timedOut,
|
|
76
|
+
argv: argv.map((part) => redact(part, secrets)),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ServiceDraft } from "../wizard/types.js";
|
|
2
|
+
|
|
3
|
+
// Symbol drift note (FN-3764): draft uses `credential` field and optional `params?: string` per endpoint.
|
|
4
|
+
export type GeneratedCliArtifact = {
|
|
5
|
+
draftId: string;
|
|
6
|
+
slug: string;
|
|
7
|
+
binPath: string;
|
|
8
|
+
entrypoint: "node" | "npx" | "direct";
|
|
9
|
+
generatedAt: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type RunRequest = {
|
|
13
|
+
endpointId: string;
|
|
14
|
+
params: Record<string, string | number | boolean>;
|
|
15
|
+
credentials?: Record<string, string>;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type RunResult = {
|
|
20
|
+
stdout: string;
|
|
21
|
+
stderr: string;
|
|
22
|
+
exitCode: number | null;
|
|
23
|
+
durationMs: number;
|
|
24
|
+
timedOut: boolean;
|
|
25
|
+
argv: string[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type GenerateCliInput = {
|
|
29
|
+
draft: ServiceDraft;
|
|
30
|
+
outDir: string;
|
|
31
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { definePlugin } from "@fusion/plugin-sdk";
|
|
2
|
+
import { createCliPrintingPressRoutes } from "./routes/wizard-routes.js";
|
|
3
|
+
import { buildExecutorRuntimeEnv } from "./runtime/executor-runtime-env.js";
|
|
4
|
+
import { createCliPressStore, ensureCliPressSchema } from "./store/cli-press-store.js";
|
|
5
|
+
|
|
6
|
+
const storeByDb = new WeakMap<object, ReturnType<typeof createCliPressStore>>();
|
|
7
|
+
|
|
8
|
+
function getStore(taskStore: { getDatabase: () => object }) {
|
|
9
|
+
const db = taskStore.getDatabase();
|
|
10
|
+
const existing = storeByDb.get(db);
|
|
11
|
+
if (existing) return existing;
|
|
12
|
+
const next = createCliPressStore(db as never);
|
|
13
|
+
storeByDb.set(db, next);
|
|
14
|
+
return next;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const plugin = definePlugin({
|
|
18
|
+
manifest: {
|
|
19
|
+
id: "fusion-plugin-cli-printing-press",
|
|
20
|
+
name: "CLI Printing Press",
|
|
21
|
+
version: "0.1.0",
|
|
22
|
+
description: "Guided wizard for drafting external service CLI definitions",
|
|
23
|
+
},
|
|
24
|
+
state: "installed",
|
|
25
|
+
hooks: {
|
|
26
|
+
onSchemaInit: ensureCliPressSchema,
|
|
27
|
+
},
|
|
28
|
+
routes: createCliPrintingPressRoutes(),
|
|
29
|
+
executorRuntimeEnv: (taskCtx, ctx) => {
|
|
30
|
+
const store = getStore(ctx.taskStore as { getDatabase: () => object });
|
|
31
|
+
return buildExecutorRuntimeEnv(store, taskCtx, ctx);
|
|
32
|
+
},
|
|
33
|
+
dashboardViews: [
|
|
34
|
+
{
|
|
35
|
+
viewId: "wizard",
|
|
36
|
+
label: "Create Service CLI",
|
|
37
|
+
componentPath: "./dashboard-view",
|
|
38
|
+
icon: "Wand2",
|
|
39
|
+
placement: "primary",
|
|
40
|
+
order: 60,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
viewId: "manage",
|
|
44
|
+
label: "Manage Service CLIs",
|
|
45
|
+
componentPath: "./manage-view",
|
|
46
|
+
icon: "List",
|
|
47
|
+
placement: "primary",
|
|
48
|
+
order: 61,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default plugin;
|
|
54
|
+
export { CliPrintingPressWizardView } from "./dashboard-view.js";
|
|
55
|
+
export { CliPrintingPressManageView } from "./manage-view.js";
|
|
56
|
+
export { CliPrintingPressTestRunner } from "./run/TestRunnerPanel.js";
|
|
57
|
+
export { createCliPressStore, ensureCliPressSchema } from "./store/cli-press-store.js";
|
|
58
|
+
export * from "./store/cli-press-types.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import { BasicsStep, CredentialsStep, EndpointsStep, TransportStep } from "../wizard/steps.js";
|
|
3
|
+
import type { ServiceDraft, WizardStep } from "../wizard/types.js";
|
|
4
|
+
import { validateBasics, validateCredentials, validateDraft, validateEndpoints, validateTransport } from "../wizard/validation.js";
|
|
5
|
+
|
|
6
|
+
const STEPS: WizardStep[] = ["basics", "transport", "endpoints", "credentials", "review"];
|
|
7
|
+
|
|
8
|
+
export function EditDraftModal({
|
|
9
|
+
initialDraft,
|
|
10
|
+
onClose,
|
|
11
|
+
onSave,
|
|
12
|
+
}: {
|
|
13
|
+
initialDraft: ServiceDraft;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
onSave: (draft: ServiceDraft) => Promise<void>;
|
|
16
|
+
}) {
|
|
17
|
+
const [draft, setDraft] = useState<ServiceDraft>(initialDraft);
|
|
18
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
19
|
+
const [error, setError] = useState<string | null>(null);
|
|
20
|
+
const [saving, setSaving] = useState(false);
|
|
21
|
+
|
|
22
|
+
const currentStep = STEPS[stepIndex];
|
|
23
|
+
const currentValidation = useMemo(() => {
|
|
24
|
+
if (currentStep === "basics") return validateBasics(draft);
|
|
25
|
+
if (currentStep === "transport") return validateTransport(draft);
|
|
26
|
+
if (currentStep === "endpoints") return validateEndpoints(draft);
|
|
27
|
+
if (currentStep === "credentials") return validateCredentials(draft.credential);
|
|
28
|
+
return { ok: true } as const;
|
|
29
|
+
}, [currentStep, draft]);
|
|
30
|
+
|
|
31
|
+
async function saveDraft() {
|
|
32
|
+
const validation = validateDraft(draft);
|
|
33
|
+
if (!validation.ok) {
|
|
34
|
+
setError(Object.values(validation.errors)[0] ?? "Draft validation failed");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
setSaving(true);
|
|
38
|
+
setError(null);
|
|
39
|
+
try {
|
|
40
|
+
await onSave(draft);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
setError(err instanceof Error ? err.message : "Failed to save draft");
|
|
43
|
+
} finally {
|
|
44
|
+
setSaving(false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="modal-overlay open" role="dialog" aria-modal="true">
|
|
50
|
+
<div className="modal modal-lg">
|
|
51
|
+
<div className="modal-header">
|
|
52
|
+
<h2>Edit Service CLI Draft</h2>
|
|
53
|
+
<button className="modal-close" onClick={onClose} aria-label="Close edit modal">×</button>
|
|
54
|
+
</div>
|
|
55
|
+
{error ? <p className="form-error">{error}</p> : null}
|
|
56
|
+
|
|
57
|
+
{currentStep === "basics" ? <BasicsStep draft={draft} onChange={(patch) => setDraft((current) => ({ ...current, ...patch }))} /> : null}
|
|
58
|
+
{currentStep === "transport" ? <TransportStep /> : null}
|
|
59
|
+
{currentStep === "endpoints" ? <EndpointsStep draft={draft} onChange={(endpoints) => setDraft((current) => ({ ...current, endpoints }))} /> : null}
|
|
60
|
+
{currentStep === "credentials" ? <CredentialsStep draft={draft} onChange={(credential) => setDraft((current) => ({ ...current, credential }))} /> : null}
|
|
61
|
+
{currentStep === "review" ? <pre className="card cli-press-manage-json-preview">{JSON.stringify(draft, null, 2)}</pre> : null}
|
|
62
|
+
|
|
63
|
+
<div className="modal-actions">
|
|
64
|
+
<button className="btn" onClick={onClose}>Cancel</button>
|
|
65
|
+
<button className="btn" disabled={stepIndex === 0 || saving} onClick={() => setStepIndex((value) => Math.max(0, value - 1))}>Back</button>
|
|
66
|
+
{stepIndex < STEPS.length - 1 ? (
|
|
67
|
+
<button className="btn btn-primary" disabled={!currentValidation.ok || saving} onClick={() => setStepIndex((value) => Math.min(STEPS.length - 1, value + 1))}>Next</button>
|
|
68
|
+
) : (
|
|
69
|
+
<button className="btn btn-primary" disabled={saving} onClick={() => void saveDraft()}>{saving ? "Saving…" : "Save"}</button>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import type { GeneratedCliArtifact } from "../generation/types.js";
|
|
3
|
+
import type { ServiceDraft } from "../wizard/types.js";
|
|
4
|
+
|
|
5
|
+
export interface DraftListItem {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const BASE_PATH = "/api/plugins/fusion-plugin-cli-printing-press/drafts";
|
|
13
|
+
|
|
14
|
+
async function parseError(response: Response, fallback: string): Promise<string> {
|
|
15
|
+
const body = await response.json().catch(() => ({} as { error?: string }));
|
|
16
|
+
return body.error ?? fallback;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useDrafts() {
|
|
20
|
+
const [drafts, setDrafts] = useState<DraftListItem[]>([]);
|
|
21
|
+
const [loading, setLoading] = useState(true);
|
|
22
|
+
const [error, setError] = useState<string | null>(null);
|
|
23
|
+
|
|
24
|
+
const refresh = useCallback(async (signal?: AbortSignal) => {
|
|
25
|
+
setLoading(true);
|
|
26
|
+
setError(null);
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(BASE_PATH, { signal });
|
|
29
|
+
if (!response.ok) throw new Error(await parseError(response, "Failed to load drafts"));
|
|
30
|
+
setDrafts(await response.json() as DraftListItem[]);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if ((err as Error).name === "AbortError") return;
|
|
33
|
+
setError(err instanceof Error ? err.message : "Failed to load drafts");
|
|
34
|
+
} finally {
|
|
35
|
+
setLoading(false);
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
void refresh(controller.signal);
|
|
42
|
+
return () => controller.abort();
|
|
43
|
+
}, [refresh]);
|
|
44
|
+
|
|
45
|
+
const getDraft = useCallback(async (id: string): Promise<ServiceDraft> => {
|
|
46
|
+
const response = await fetch(`${BASE_PATH}/${id}`);
|
|
47
|
+
if (!response.ok) throw new Error(await parseError(response, "Failed to load draft"));
|
|
48
|
+
return await response.json() as ServiceDraft;
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
const updateDraft = useCallback(async (id: string, draft: ServiceDraft): Promise<ServiceDraft> => {
|
|
52
|
+
const response = await fetch(`${BASE_PATH}/${id}`, {
|
|
53
|
+
method: "PUT",
|
|
54
|
+
headers: { "content-type": "application/json" },
|
|
55
|
+
body: JSON.stringify(draft),
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) throw new Error(await parseError(response, "Failed to update draft"));
|
|
58
|
+
return await response.json() as ServiceDraft;
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
const regenerateDraft = useCallback(async (id: string): Promise<{ draft: ServiceDraft; artifact: GeneratedCliArtifact }> => {
|
|
62
|
+
const response = await fetch(`${BASE_PATH}/${id}/regenerate`, { method: "POST" });
|
|
63
|
+
if (!response.ok) throw new Error(await parseError(response, "Failed to regenerate draft"));
|
|
64
|
+
return await response.json() as { draft: ServiceDraft; artifact: GeneratedCliArtifact };
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
const deleteDraft = useCallback(async (id: string): Promise<void> => {
|
|
68
|
+
const response = await fetch(`${BASE_PATH}/${id}`, { method: "DELETE" });
|
|
69
|
+
if (!response.ok && response.status !== 204) throw new Error(await parseError(response, "Failed to delete draft"));
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
return { drafts, loading, error, refresh, getDraft, updateDraft, regenerateDraft, deleteDraft };
|
|
73
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
.cli-press-manage {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-md);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.cli-press-manage-header h2 {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--space-sm);
|
|
11
|
+
margin: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.cli-press-manage-layout {
|
|
15
|
+
display: grid;
|
|
16
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
17
|
+
gap: var(--space-md);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.cli-press-manage-list {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
gap: var(--space-sm);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.cli-press-manage-row {
|
|
27
|
+
width: 100%;
|
|
28
|
+
text-align: left;
|
|
29
|
+
border: var(--btn-border-width) solid var(--border);
|
|
30
|
+
background: var(--card);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.cli-press-manage-row.is-selected {
|
|
34
|
+
box-shadow: var(--focus-ring-strong);
|
|
35
|
+
border-color: var(--todo);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cli-press-manage-row-title {
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.cli-press-manage-row-meta {
|
|
43
|
+
color: var(--text-muted);
|
|
44
|
+
font-size: 0.875em;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.cli-press-manage-detail {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: var(--space-sm);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.cli-press-manage-actions {
|
|
54
|
+
display: flex;
|
|
55
|
+
gap: var(--space-sm);
|
|
56
|
+
flex-wrap: wrap;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.cli-press-manage-status {
|
|
60
|
+
color: var(--text);
|
|
61
|
+
background: var(--status-in-progress-bg);
|
|
62
|
+
border-radius: var(--radius-md);
|
|
63
|
+
padding: var(--space-sm) var(--space-md);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.cli-press-manage-state {
|
|
67
|
+
padding: var(--space-lg);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.cli-press-manage-json-preview {
|
|
71
|
+
max-height: 40vh;
|
|
72
|
+
overflow: auto;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@media (max-width: 768px) {
|
|
76
|
+
.cli-press-manage-layout {
|
|
77
|
+
grid-template-columns: minmax(0, 1fr);
|
|
78
|
+
}
|
|
79
|
+
}
|