@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.
Files changed (171) hide show
  1. package/dist/bin.js +11036 -1992
  2. package/dist/client/assets/AgentDetailView-B7QRcHJH.css +1 -0
  3. package/dist/client/assets/AgentDetailView-DwLmRXTY.js +18 -0
  4. package/dist/client/assets/{AgentsView-D6Zi5zfP.js → AgentsView-D-N6aA0P.js} +12 -7
  5. package/dist/client/assets/ChatView-DnCdKu8Z.js +1 -0
  6. package/dist/client/assets/{DevServerView--_WBvIDQ.js → DevServerView-BiA1nYtt.js} +1 -1
  7. package/dist/client/assets/{DirectoryPicker-xedtR-Rd.js → DirectoryPicker-DvBviDG6.js} +1 -1
  8. package/dist/client/assets/{DocumentsView-Bg2oaZks.js → DocumentsView-BWXOxpuq.js} +1 -1
  9. package/dist/client/assets/{EvalsView-B3uOCXfr.js → EvalsView-CJFbtL7i.js} +1 -1
  10. package/dist/client/assets/{ExperimentalAgentOnboardingModal-Bx6yXVS5.js → ExperimentalAgentOnboardingModal-DuGIPd0B.js} +1 -1
  11. package/dist/client/assets/InsightsView-BBpRiolN.js +11 -0
  12. package/dist/client/assets/{MemoryView-xcN_eouf.js → MemoryView-48LuNkKk.js} +2 -2
  13. package/dist/client/assets/NodesView-CGQWSNZM.js +14 -0
  14. package/dist/client/assets/{PiExtensionsManager-Cc8aAZXg.js → PiExtensionsManager-i-7UL2oh.js} +2 -2
  15. package/dist/client/assets/PluginManager-DoSAykD6.js +1 -0
  16. package/dist/client/assets/{ResearchView-CERNf7sJ.js → ResearchView-XZuRtOxE.js} +1 -1
  17. package/dist/client/assets/{SettingsModal-Cis-4Lot.css → SettingsModal-Ci0_sqbU.css} +1 -1
  18. package/dist/client/assets/{SettingsModal-B1r0yASu.js → SettingsModal-CmeF8CN4.js} +1 -1
  19. package/dist/client/assets/SettingsModal-DBcjf9Bu.js +31 -0
  20. package/dist/client/assets/SettingsModal-DWKgRxBA.css +1 -0
  21. package/dist/client/assets/{SetupWizardModal-D1q548_L.js → SetupWizardModal-CgtvpMX9.js} +1 -1
  22. package/dist/client/assets/{SkillsView-ClLM6u6p.js → SkillsView-DErYRumF.js} +1 -1
  23. package/dist/client/assets/{StashRecoveryView-ze0pEZ5U.js → StashRecoveryView-QJrNS4Vg.js} +1 -1
  24. package/dist/client/assets/{TodoView-CTmIfy2M.js → TodoView-BD9NRwq0.js} +2 -2
  25. package/dist/client/assets/{dashboard-view-CyWN-d02.js → dashboard-view-BWGH_fAq.js} +1 -1
  26. package/dist/client/assets/dashboard-view-BoTzlP8b.css +1 -0
  27. package/dist/client/assets/dashboard-view-Ws9_ZnKu.js +21 -0
  28. package/dist/client/assets/{folder-open-BZuKESeq.js → folder-open-CHSlllzf.js} +1 -1
  29. package/dist/client/assets/index-DCovGm5b.css +1 -0
  30. package/dist/client/assets/index-bEwSVl7B.js +692 -0
  31. package/dist/client/assets/{star-D75YKEq-.js → star-BgVwWAPz.js} +1 -1
  32. package/dist/client/assets/{upload-BYYTgWFj.js → upload-CAzycxr9.js} +1 -1
  33. package/dist/client/assets/{users-RS90Aii3.js → users-CZnxCCCJ.js} +1 -1
  34. package/dist/client/index.html +2 -2
  35. package/dist/client/version.json +1 -1
  36. package/dist/droid-cli/package.json +1 -1
  37. package/dist/droid-cli/src/__tests__/index.test.ts +228 -0
  38. package/dist/extension.js +5517 -1193
  39. package/dist/pi-claude-cli/package.json +1 -1
  40. package/dist/pi-claude-cli/src/__tests__/provider.test.ts +36 -22
  41. package/dist/pi-claude-cli/src/provider.ts +7 -1
  42. package/dist/plugins/fusion-plugin-cli-printing-press/manifest.json +19 -1
  43. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +20 -2
  44. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/TestRunnerPanel.test.tsx +99 -0
  45. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/config-flow.test.ts +91 -0
  46. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-view.test.tsx +40 -0
  47. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-views.test.ts +46 -0
  48. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/draft-store.test.ts +50 -0
  49. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/exec-mock.ts +80 -0
  50. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/fixtures.test.ts +40 -0
  51. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/registry.ts +82 -0
  52. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/generator.test.ts +54 -0
  53. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manage-view.test.tsx +98 -0
  54. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manifest.test.ts +21 -5
  55. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/registration.test.ts +29 -0
  56. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/run-routes.test.ts +98 -0
  57. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runner.test.ts +55 -0
  58. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runtime-availability.test.ts +61 -0
  59. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/validation.test.ts +30 -0
  60. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/wizard-routes.test.ts +61 -0
  61. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/workflow-integration.test.ts +19 -0
  62. package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.css +43 -0
  63. package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.tsx +49 -0
  64. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/generator.ts +95 -0
  65. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/redact.ts +9 -0
  66. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/runner.ts +79 -0
  67. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/types.ts +31 -0
  68. package/dist/plugins/fusion-plugin-cli-printing-press/src/index.ts +46 -2
  69. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/EditDraftModal.tsx +75 -0
  70. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/useDrafts.ts +73 -0
  71. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.css +79 -0
  72. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.tsx +122 -0
  73. package/dist/plugins/fusion-plugin-cli-printing-press/src/routes/wizard-routes.ts +272 -0
  74. package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.css +70 -0
  75. package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.tsx +98 -0
  76. package/dist/plugins/fusion-plugin-cli-printing-press/src/run/useRunGeneratedCli.ts +37 -0
  77. package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/__tests__/executor-runtime-env.test.ts +191 -0
  78. package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts +75 -0
  79. package/dist/plugins/fusion-plugin-cli-printing-press/src/storage/draft-store.ts +85 -0
  80. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/cli-press-store.test.ts +128 -0
  81. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/credentials.test.ts +62 -0
  82. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts +427 -0
  83. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-types.ts +110 -0
  84. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/credentials.ts +95 -0
  85. package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/steps.tsx +55 -0
  86. package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/types.ts +33 -0
  87. package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/validation.ts +63 -0
  88. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  89. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  90. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  91. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  92. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  93. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  94. package/dist/plugins/fusion-plugin-reports/manifest.json +10 -0
  95. package/dist/plugins/fusion-plugin-reports/package.json +18 -2
  96. package/dist/plugins/fusion-plugin-reports/src/__tests__/approval.test.ts +164 -0
  97. package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +14 -0
  98. package/dist/plugins/fusion-plugin-reports/src/__tests__/routes-approval.test.ts +109 -0
  99. package/dist/plugins/fusion-plugin-reports/src/__tests__/scaffold.test.ts +60 -0
  100. package/dist/plugins/fusion-plugin-reports/src/__tests__/share-blocks.test.ts +83 -0
  101. package/dist/plugins/fusion-plugin-reports/src/aggregation.ts +23 -0
  102. package/dist/plugins/fusion-plugin-reports/src/approval.ts +97 -0
  103. package/dist/plugins/fusion-plugin-reports/src/cadence.ts +23 -0
  104. package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.css +82 -0
  105. package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.tsx +24 -0
  106. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportComparisonDrawer.test.tsx +12 -0
  107. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportDetailPanel.test.tsx +12 -0
  108. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportFiltersBar.test.tsx +14 -0
  109. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportsView.test.tsx +27 -0
  110. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/api.test.ts +19 -0
  111. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReportSectionDiff.test.ts +11 -0
  112. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReports.test.ts +13 -0
  113. package/dist/plugins/fusion-plugin-reports/src/dashboard/api.ts +85 -0
  114. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.css +59 -0
  115. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.tsx +58 -0
  116. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportComparisonDrawer.tsx +21 -0
  117. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportDetailPanel.tsx +29 -0
  118. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportEmptyState.tsx +3 -0
  119. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportFiltersBar.tsx +19 -0
  120. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportListItem.tsx +8 -0
  121. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.css +29 -0
  122. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.tsx +43 -0
  123. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ReportApprovalPanel.test.tsx +38 -0
  124. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ShareBlocksPanel.test.tsx +24 -0
  125. package/dist/plugins/fusion-plugin-reports/src/dashboard/test-setup.ts +18 -0
  126. package/dist/plugins/fusion-plugin-reports/src/dashboard/types.ts +22 -0
  127. package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportPreview.ts +44 -0
  128. package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportSectionDiff.ts +59 -0
  129. package/dist/plugins/fusion-plugin-reports/src/dashboard/useReports.ts +71 -0
  130. package/dist/plugins/fusion-plugin-reports/src/dashboard/useViewportMode.ts +13 -0
  131. package/dist/plugins/fusion-plugin-reports/src/dashboard-view.tsx +6 -0
  132. package/dist/plugins/fusion-plugin-reports/src/index.ts +48 -2
  133. package/dist/plugins/fusion-plugin-reports/src/pipeline.ts +58 -0
  134. package/dist/plugins/fusion-plugin-reports/src/render/__tests__/escape.test.ts +20 -0
  135. package/dist/plugins/fusion-plugin-reports/src/render/__tests__/html-template.test.ts +110 -0
  136. package/dist/plugins/fusion-plugin-reports/src/render/__tests__/standalone-html.test.ts +66 -0
  137. package/dist/plugins/fusion-plugin-reports/src/render/escape.ts +12 -0
  138. package/dist/plugins/fusion-plugin-reports/src/render/html-styles.ts +40 -0
  139. package/dist/plugins/fusion-plugin-reports/src/render/html-template.ts +137 -0
  140. package/dist/plugins/fusion-plugin-reports/src/render/index.ts +4 -0
  141. package/dist/plugins/fusion-plugin-reports/src/render/standalone-html.ts +75 -0
  142. package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +31 -0
  143. package/dist/plugins/fusion-plugin-reports/src/routes/__tests__/report-export-routes.test.ts +104 -0
  144. package/dist/plugins/fusion-plugin-reports/src/routes/report-approval-routes.ts +98 -0
  145. package/dist/plugins/fusion-plugin-reports/src/routes/report-export-routes.ts +77 -0
  146. package/dist/plugins/fusion-plugin-reports/src/routes/report-list-routes.ts +72 -0
  147. package/dist/plugins/fusion-plugin-reports/src/runs-store.ts +69 -0
  148. package/dist/plugins/fusion-plugin-reports/src/share-blocks.ts +82 -0
  149. package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +51 -2
  150. package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +6 -1
  151. package/dist/plugins/fusion-plugin-roadmap/bundled.js +1528 -29391
  152. package/dist/plugins/fusion-plugin-roadmap/manifest.json +1 -1
  153. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  154. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  155. package/package.json +1 -1
  156. package/skill/fusion/references/engine-tools.md +1 -1
  157. package/skill/fusion/references/extension-tools.md +3 -3
  158. package/skill/fusion/references/fusion-capabilities.md +1 -1
  159. package/dist/client/assets/AgentDetailView-BwJaLqZh.css +0 -1
  160. package/dist/client/assets/AgentDetailView-Cv-vgOj3.js +0 -18
  161. package/dist/client/assets/ChatView-CAHjY9uO.js +0 -1
  162. package/dist/client/assets/InsightsView-Q1zvtF4F.js +0 -11
  163. package/dist/client/assets/NodesView-RxXg58_Q.js +0 -14
  164. package/dist/client/assets/PluginManager-BEkyBajl.js +0 -1
  165. package/dist/client/assets/SettingsModal-BLsac7CJ.js +0 -31
  166. package/dist/client/assets/SettingsModal-BNSrO1M9.css +0 -1
  167. package/dist/client/assets/dashboard-view-4xAN3yO5.js +0 -21
  168. package/dist/client/assets/dashboard-view-BkTMSZYn.css +0 -1
  169. package/dist/client/assets/index-Bdw6llW6.js +0 -692
  170. package/dist/client/assets/index-CZGlyJuS.css +0 -1
  171. package/dist/plugins/fusion-plugin-roadmap/bundled.css +0 -1093
@@ -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
+ };
@@ -1,14 +1,58 @@
1
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
+ }
2
16
 
3
17
  const plugin = definePlugin({
4
18
  manifest: {
5
19
  id: "fusion-plugin-cli-printing-press",
6
20
  name: "CLI Printing Press",
7
21
  version: "0.1.0",
8
- description: "Generate and manage CLIs for external services using cli-printing-press",
22
+ description: "Guided wizard for drafting external service CLI definitions",
9
23
  },
10
24
  state: "installed",
11
- hooks: {},
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
+ ],
12
51
  });
13
52
 
14
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
+ }