@xdelivered/emberflow 0.2.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/LICENSE +21 -0
- package/README.md +153 -0
- package/bin/commands.test.ts +185 -0
- package/bin/commands.ts +306 -0
- package/bin/emberflow.mjs +38 -0
- package/bin/init.test.ts +165 -0
- package/bin/init.ts +297 -0
- package/bin/runtime.mjs +105 -0
- package/bin/runtime.test.ts +167 -0
- package/bin/skillTargets.test.ts +108 -0
- package/bin/skillTargets.ts +36 -0
- package/bin/skillTemplates.test.ts +37 -0
- package/bin/tsconfig.json +23 -0
- package/dist/bin/commands.d.ts +24 -0
- package/dist/bin/commands.js +252 -0
- package/dist/bin/init.d.ts +17 -0
- package/dist/bin/init.js +241 -0
- package/dist/bin/runtime.d.mts +39 -0
- package/dist/bin/runtime.mjs +96 -0
- package/dist/bin/skillTargets.d.ts +13 -0
- package/dist/bin/skillTargets.js +22 -0
- package/dist/server/agents/claudeAdapter.d.ts +8 -0
- package/dist/server/agents/claudeAdapter.js +190 -0
- package/dist/server/agents/claudeParse.d.ts +2 -0
- package/dist/server/agents/claudeParse.js +162 -0
- package/dist/server/agents/codexAdapter.d.ts +13 -0
- package/dist/server/agents/codexAdapter.js +174 -0
- package/dist/server/agents/codexParse.d.ts +2 -0
- package/dist/server/agents/codexParse.js +74 -0
- package/dist/server/agents/detect.d.ts +23 -0
- package/dist/server/agents/detect.js +35 -0
- package/dist/server/agents/gitScope.d.ts +24 -0
- package/dist/server/agents/gitScope.js +95 -0
- package/dist/server/agents/modelRejectionHint.d.ts +8 -0
- package/dist/server/agents/modelRejectionHint.js +12 -0
- package/dist/server/agents/prompt.d.ts +79 -0
- package/dist/server/agents/prompt.js +364 -0
- package/dist/server/agents/runManager.d.ts +90 -0
- package/dist/server/agents/runManager.js +259 -0
- package/dist/server/agents/types.d.ts +15 -0
- package/dist/server/agents/types.js +1 -0
- package/dist/server/apiStore.d.ts +60 -0
- package/dist/server/apiStore.js +249 -0
- package/dist/server/auth/enforce.d.ts +1 -0
- package/dist/server/auth/enforce.js +6 -0
- package/dist/server/auth/types.d.ts +1 -0
- package/dist/server/auth/types.js +1 -0
- package/dist/server/auth/verifiers.d.ts +1 -0
- package/dist/server/auth/verifiers.js +4 -0
- package/dist/server/authAttach.d.ts +19 -0
- package/dist/server/authAttach.js +79 -0
- package/dist/server/cli.d.ts +7 -0
- package/dist/server/cli.js +804 -0
- package/dist/server/client.d.ts +130 -0
- package/dist/server/client.js +247 -0
- package/dist/server/environments.d.ts +103 -0
- package/dist/server/environments.js +486 -0
- package/dist/server/flowStore.d.ts +37 -0
- package/dist/server/flowStore.js +117 -0
- package/dist/server/httpOperations.d.ts +32 -0
- package/dist/server/httpOperations.js +62 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1022 -0
- package/dist/server/infrastructure.d.ts +51 -0
- package/dist/server/infrastructure.js +116 -0
- package/dist/server/login.d.ts +21 -0
- package/dist/server/login.js +44 -0
- package/dist/server/mcp.d.ts +1 -0
- package/dist/server/mcp.js +182 -0
- package/dist/server/migrateFlows.d.ts +8 -0
- package/dist/server/migrateFlows.js +25 -0
- package/dist/server/mockHandler.d.ts +15 -0
- package/dist/server/mockHandler.js +35 -0
- package/dist/server/nodesPayload.d.ts +14 -0
- package/dist/server/nodesPayload.js +14 -0
- package/dist/server/normalizeFlow.d.ts +21 -0
- package/dist/server/normalizeFlow.js +48 -0
- package/dist/server/openBrowser.d.ts +8 -0
- package/dist/server/openBrowser.js +16 -0
- package/dist/server/operationResult.d.ts +12 -0
- package/dist/server/operationResult.js +26 -0
- package/dist/server/pathGuard.d.ts +1 -0
- package/dist/server/pathGuard.js +16 -0
- package/dist/server/pathSafety.d.ts +20 -0
- package/dist/server/pathSafety.js +35 -0
- package/dist/server/projectConfig.d.ts +47 -0
- package/dist/server/projectConfig.js +61 -0
- package/dist/server/projectMode.d.ts +26 -0
- package/dist/server/projectMode.js +44 -0
- package/dist/server/redact.d.ts +6 -0
- package/dist/server/redact.js +37 -0
- package/dist/server/runRegistry.d.ts +84 -0
- package/dist/server/runRegistry.js +200 -0
- package/dist/server/runtime/RuntimeAdapter.d.ts +22 -0
- package/dist/server/runtime/RuntimeAdapter.js +1 -0
- package/dist/server/runtime/expressAdapter.d.ts +19 -0
- package/dist/server/runtime/expressAdapter.js +56 -0
- package/dist/server/scenarioTest.d.ts +14 -0
- package/dist/server/scenarioTest.js +66 -0
- package/dist/server/subflowRunner.d.ts +32 -0
- package/dist/server/subflowRunner.js +72 -0
- package/dist/server/testRunner.d.ts +70 -0
- package/dist/server/testRunner.js +151 -0
- package/dist/src/config.d.ts +3 -0
- package/dist/src/config.js +2 -0
- package/dist/src/engine/authVerify.d.ts +33 -0
- package/dist/src/engine/authVerify.js +63 -0
- package/dist/src/engine/diagnostics.d.ts +62 -0
- package/dist/src/engine/diagnostics.js +148 -0
- package/dist/src/engine/executor.d.ts +151 -0
- package/dist/src/engine/executor.js +684 -0
- package/dist/src/engine/httpError.d.ts +7 -0
- package/dist/src/engine/httpError.js +14 -0
- package/dist/src/engine/index.d.ts +11 -0
- package/dist/src/engine/index.js +11 -0
- package/dist/src/engine/output.d.ts +11 -0
- package/dist/src/engine/output.js +20 -0
- package/dist/src/engine/publish.d.ts +30 -0
- package/dist/src/engine/publish.js +95 -0
- package/dist/src/engine/registry.d.ts +34 -0
- package/dist/src/engine/registry.js +67 -0
- package/dist/src/engine/scenarios.d.ts +12 -0
- package/dist/src/engine/scenarios.js +19 -0
- package/dist/src/engine/schemaCheck.d.ts +4 -0
- package/dist/src/engine/schemaCheck.js +34 -0
- package/dist/src/engine/trace.d.ts +12 -0
- package/dist/src/engine/trace.js +12 -0
- package/dist/src/engine/types.d.ts +308 -0
- package/dist/src/engine/types.js +1 -0
- package/dist/src/engine/validation.d.ts +26 -0
- package/dist/src/engine/validation.js +318 -0
- package/dist/src/flows/anomaly-flows.d.ts +23 -0
- package/dist/src/flows/anomaly-flows.js +151 -0
- package/dist/src/flows/city-sweep-flow.d.ts +10 -0
- package/dist/src/flows/city-sweep-flow.js +127 -0
- package/dist/src/flows/login-flow.d.ts +13 -0
- package/dist/src/flows/login-flow.js +145 -0
- package/dist/src/flows/pradar-flows.d.ts +5 -0
- package/dist/src/flows/pradar-flows.js +375 -0
- package/dist/src/flows/weather-flow.d.ts +12 -0
- package/dist/src/flows/weather-flow.js +150 -0
- package/dist/src/lib/pathParams.d.ts +3 -0
- package/dist/src/lib/pathParams.js +6 -0
- package/dist/src/nodes/anomaly.d.ts +2 -0
- package/dist/src/nodes/anomaly.js +556 -0
- package/dist/src/nodes/flow-control.d.ts +7 -0
- package/dist/src/nodes/flow-control.js +270 -0
- package/dist/src/nodes/index.d.ts +3 -0
- package/dist/src/nodes/index.js +20 -0
- package/dist/src/nodes/login.d.ts +7 -0
- package/dist/src/nodes/login.js +158 -0
- package/dist/src/nodes/loops.d.ts +11 -0
- package/dist/src/nodes/loops.js +60 -0
- package/dist/src/nodes/pradar/index.d.ts +7 -0
- package/dist/src/nodes/pradar/index.js +9 -0
- package/dist/src/nodes/pradar/logic.d.ts +189 -0
- package/dist/src/nodes/pradar/logic.js +370 -0
- package/dist/src/nodes/pradar/nodes.d.ts +42 -0
- package/dist/src/nodes/pradar/nodes.js +801 -0
- package/dist/src/nodes/requireAuth.d.ts +20 -0
- package/dist/src/nodes/requireAuth.js +48 -0
- package/dist/src/nodes/response.d.ts +8 -0
- package/dist/src/nodes/response.js +33 -0
- package/dist/src/nodes/weather.d.ts +33 -0
- package/dist/src/nodes/weather.js +260 -0
- package/package.json +92 -0
- package/server/agentRoute.test.ts +157 -0
- package/server/agents/__fixtures__/fake-claude-writer.mjs +23 -0
- package/server/agents/__fixtures__/fake-claude.mjs +55 -0
- package/server/agents/__fixtures__/fake-codex-writer.mjs +27 -0
- package/server/agents/__fixtures__/fake-codex.mjs +52 -0
- package/server/agents/__fixtures__/fake-version-empty.mjs +3 -0
- package/server/agents/__fixtures__/fake-version-fail.mjs +4 -0
- package/server/agents/__fixtures__/fake-version-garbage.mjs +4 -0
- package/server/agents/__fixtures__/fake-version-good.mjs +4 -0
- package/server/agents/__fixtures__/fake-version-hang.mjs +3 -0
- package/server/agents/claudeAdapter.test.ts +127 -0
- package/server/agents/claudeAdapter.ts +212 -0
- package/server/agents/claudeParse.ts +193 -0
- package/server/agents/codexAdapter.test.ts +125 -0
- package/server/agents/codexAdapter.ts +198 -0
- package/server/agents/codexParse.test.ts +58 -0
- package/server/agents/codexParse.ts +126 -0
- package/server/agents/detect.test.ts +65 -0
- package/server/agents/detect.ts +43 -0
- package/server/agents/gitScope.test.ts +129 -0
- package/server/agents/gitScope.ts +110 -0
- package/server/agents/modelRejectionHint.test.ts +30 -0
- package/server/agents/modelRejectionHint.ts +14 -0
- package/server/agents/prompt.test.ts +613 -0
- package/server/agents/prompt.ts +527 -0
- package/server/agents/runManager.test.ts +251 -0
- package/server/agents/runManager.ts +286 -0
- package/server/agents/types.ts +17 -0
- package/server/apiMount.test.ts +591 -0
- package/server/apiStore.test.ts +324 -0
- package/server/apiStore.ts +241 -0
- package/server/auth/enforce.test.ts +61 -0
- package/server/auth/enforce.ts +6 -0
- package/server/auth/types.ts +5 -0
- package/server/auth/verifiers.test.ts +37 -0
- package/server/auth/verifiers.ts +9 -0
- package/server/authAttach.test.ts +175 -0
- package/server/authAttach.ts +99 -0
- package/server/cli.ts +847 -0
- package/server/cliAuth.test.ts +229 -0
- package/server/cliCreate.test.ts +131 -0
- package/server/cliDoctor.test.ts +326 -0
- package/server/cliTest.test.ts +735 -0
- package/server/client.ts +314 -0
- package/server/demoProject.test.ts +29 -0
- package/server/diagnosticsRoute.test.ts +169 -0
- package/server/environments.test.ts +620 -0
- package/server/environments.ts +603 -0
- package/server/environmentsRoute.test.ts +385 -0
- package/server/errorWorkflow.test.ts +205 -0
- package/server/flowStore.test.ts +84 -0
- package/server/flowStore.ts +125 -0
- package/server/httpOperations.test.ts +377 -0
- package/server/httpOperations.ts +96 -0
- package/server/index.ts +1111 -0
- package/server/infrastructure.test.ts +154 -0
- package/server/infrastructure.ts +161 -0
- package/server/infrastructureRoute.test.ts +105 -0
- package/server/login.test.ts +153 -0
- package/server/login.ts +67 -0
- package/server/mcp.ts +278 -0
- package/server/migrateFlows.test.ts +55 -0
- package/server/migrateFlows.ts +23 -0
- package/server/mockHandler.test.ts +138 -0
- package/server/mockHandler.ts +52 -0
- package/server/mockRun.test.ts +367 -0
- package/server/mockServe.test.ts +467 -0
- package/server/nodesPayload.test.ts +29 -0
- package/server/nodesPayload.ts +26 -0
- package/server/normalizeFlow.test.ts +80 -0
- package/server/normalizeFlow.ts +59 -0
- package/server/openBrowser.test.ts +20 -0
- package/server/openBrowser.ts +22 -0
- package/server/operationResult.test.ts +67 -0
- package/server/operationResult.ts +27 -0
- package/server/pathGuard.test.ts +19 -0
- package/server/pathGuard.ts +12 -0
- package/server/pathSafety.test.ts +38 -0
- package/server/pathSafety.ts +33 -0
- package/server/projectConfig.test.ts +67 -0
- package/server/projectConfig.ts +101 -0
- package/server/projectMode.test.ts +106 -0
- package/server/projectMode.ts +59 -0
- package/server/redact.test.ts +88 -0
- package/server/redact.ts +35 -0
- package/server/redactEgress.test.ts +281 -0
- package/server/runRegistry.ts +270 -0
- package/server/runsAuth.test.ts +119 -0
- package/server/runtime/RuntimeAdapter.ts +24 -0
- package/server/runtime/expressAdapter.test.ts +38 -0
- package/server/runtime/expressAdapter.ts +70 -0
- package/server/scenarioTest.test.ts +148 -0
- package/server/scenarioTest.ts +88 -0
- package/server/setupStatus.test.ts +127 -0
- package/server/subflowRunner.ts +104 -0
- package/server/testRunner.ts +216 -0
- package/server/tsconfig.json +20 -0
- package/server/workflowTestRoute.test.ts +297 -0
- package/src/App.tsx +233 -0
- package/src/components/AgentConsole.tsx +461 -0
- package/src/components/BranchRulesEditor.tsx +156 -0
- package/src/components/CreateModal.tsx +275 -0
- package/src/components/Dock.tsx +487 -0
- package/src/components/EnvironmentDialog.tsx +569 -0
- package/src/components/EnvironmentPicker.tsx +357 -0
- package/src/components/EnvironmentsDialog.tsx +144 -0
- package/src/components/ExecutionPager.tsx +67 -0
- package/src/components/InfraPanel.test.tsx +103 -0
- package/src/components/InfraPanel.tsx +171 -0
- package/src/components/Inspector.tsx +1182 -0
- package/src/components/Json.tsx +50 -0
- package/src/components/JsonModal.tsx +52 -0
- package/src/components/LogMessage.tsx +61 -0
- package/src/components/NodeRunModal.tsx +213 -0
- package/src/components/RunLogPanel.tsx +397 -0
- package/src/components/RunbookView.tsx +1085 -0
- package/src/components/Scenarios.tsx +476 -0
- package/src/components/SettingsDialog.tsx +229 -0
- package/src/components/Sidebar.tsx +436 -0
- package/src/components/StatusBar.tsx +302 -0
- package/src/components/Toolbar.tsx +663 -0
- package/src/components/WelcomeDialog.test.tsx +107 -0
- package/src/components/WelcomeDialog.tsx +412 -0
- package/src/components/agentMarkdown.test.tsx +91 -0
- package/src/components/agentMarkdown.tsx +158 -0
- package/src/components/ui/badge.tsx +31 -0
- package/src/components/ui/button.tsx +46 -0
- package/src/components/ui/combobox.tsx +114 -0
- package/src/components/ui/command.tsx +82 -0
- package/src/components/ui/dialog.tsx +73 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/popover.tsx +32 -0
- package/src/components/ui/resizable.tsx +44 -0
- package/src/components/ui/tabs.tsx +36 -0
- package/src/config.ts +4 -0
- package/src/engine/authVerify.ts +81 -0
- package/src/engine/diagnostics.test.ts +437 -0
- package/src/engine/diagnostics.ts +211 -0
- package/src/engine/executor.test.ts +1339 -0
- package/src/engine/executor.ts +840 -0
- package/src/engine/httpError.test.ts +12 -0
- package/src/engine/httpError.ts +16 -0
- package/src/engine/index.ts +11 -0
- package/src/engine/output.ts +21 -0
- package/src/engine/publish.test.ts +176 -0
- package/src/engine/publish.ts +127 -0
- package/src/engine/registry.test.ts +64 -0
- package/src/engine/registry.ts +80 -0
- package/src/engine/scenarios.test.ts +55 -0
- package/src/engine/scenarios.ts +29 -0
- package/src/engine/schemaCheck.test.ts +21 -0
- package/src/engine/schemaCheck.ts +35 -0
- package/src/engine/trace.test.ts +19 -0
- package/src/engine/trace.ts +23 -0
- package/src/engine/types.test.ts +22 -0
- package/src/engine/types.ts +316 -0
- package/src/engine/validation.test.ts +314 -0
- package/src/engine/validation.ts +354 -0
- package/src/flows/anomaly-flows.test.ts +81 -0
- package/src/flows/anomaly-flows.ts +202 -0
- package/src/flows/city-sweep-flow.ts +131 -0
- package/src/flows/flows.test.ts +30 -0
- package/src/flows/login-flow.ts +150 -0
- package/src/flows/pradar-flows.test.ts +204 -0
- package/src/flows/pradar-flows.ts +419 -0
- package/src/flows/weather-flow.ts +154 -0
- package/src/globals.css +147 -0
- package/src/lib/pathParams.test.ts +24 -0
- package/src/lib/pathParams.ts +6 -0
- package/src/lib/registerLens.test.ts +79 -0
- package/src/lib/registerLens.ts +59 -0
- package/src/lib/runbookModel.test.ts +61 -0
- package/src/lib/runbookModel.ts +290 -0
- package/src/lib/runbookProjection.test.ts +417 -0
- package/src/lib/runbookProjection.ts +254 -0
- package/src/lib/useTailAnchor.ts +53 -0
- package/src/lib/utils.ts +7 -0
- package/src/main.tsx +13 -0
- package/src/nodes/anomaly.test.ts +98 -0
- package/src/nodes/anomaly.ts +637 -0
- package/src/nodes/effects-scan.test.ts +44 -0
- package/src/nodes/flow-control.test.ts +280 -0
- package/src/nodes/flow-control.ts +305 -0
- package/src/nodes/index.ts +22 -0
- package/src/nodes/login.test.ts +81 -0
- package/src/nodes/login.ts +186 -0
- package/src/nodes/loops.ts +69 -0
- package/src/nodes/pradar/index.ts +11 -0
- package/src/nodes/pradar/logic.test.ts +288 -0
- package/src/nodes/pradar/logic.ts +582 -0
- package/src/nodes/pradar/nodes.test.ts +146 -0
- package/src/nodes/pradar/nodes.ts +955 -0
- package/src/nodes/requireAuth.test.ts +75 -0
- package/src/nodes/requireAuth.ts +55 -0
- package/src/nodes/response.test.ts +33 -0
- package/src/nodes/response.ts +39 -0
- package/src/nodes/traceDetail.test.ts +39 -0
- package/src/nodes/weather.test.ts +65 -0
- package/src/nodes/weather.ts +306 -0
- package/src/store/agentClient.test.ts +103 -0
- package/src/store/agentClient.ts +131 -0
- package/src/store/apiTree.test.ts +17 -0
- package/src/store/apiTree.ts +73 -0
- package/src/store/builderStore.test.ts +1234 -0
- package/src/store/builderStore.ts +1879 -0
- package/src/store/infraClient.ts +63 -0
- package/src/store/nodeMeta.test.ts +24 -0
- package/src/store/nodeMeta.ts +21 -0
- package/src/store/persistence.test.ts +15 -0
- package/src/store/persistence.ts +54 -0
- package/src/store/serverRunner.ts +381 -0
- package/src/store/setupClient.ts +57 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/studio-dist/assets/index-DNJwW-hM.css +1 -0
- package/studio-dist/assets/index-O26dKRjW.js +65 -0
- package/studio-dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/studio-dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/studio-dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/studio-dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/studio-dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/studio-dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/studio-dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/studio-dist/favicon.svg +1 -0
- package/studio-dist/icons.svg +24 -0
- package/studio-dist/index.html +14 -0
- package/templates/skills/emberflow-basics/SKILL.md +316 -0
- package/templates/skills/emberflow-model-process/SKILL.md +209 -0
- package/templates/skills/emberflow-new-workflow/SKILL.md +211 -0
- package/templates/skills/emberflow-review-workflow/SKILL.md +148 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Loader2Icon, PlusIcon, ShieldIcon, SparklesIcon, XIcon } from 'lucide-react';
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/components/ui/dialog';
|
|
6
|
+
import { Input } from '@/components/ui/input';
|
|
7
|
+
import { useBuilderStore } from '../store/builderStore';
|
|
8
|
+
import type { EnvAuth, EnvironmentSummary } from '../store/serverRunner';
|
|
9
|
+
|
|
10
|
+
function SectionTitle({ children }: { children: React.ReactNode }) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="text-[10px] font-medium uppercase tracking-widest text-muted-foreground">
|
|
13
|
+
{children}
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Inline error line shared by the dialog's sections. */
|
|
19
|
+
function ErrorLine({ message }: { message: string | null }) {
|
|
20
|
+
if (!message) return null;
|
|
21
|
+
return <div className="text-[11.5px] text-destructive">{message}</div>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Secrets manager: stored values are write-only — the dialog shows key names
|
|
26
|
+
* and a set/unset state, never a value. New values go straight to the runner
|
|
27
|
+
* (PUT …/secrets/:key) and exist in studio memory only for the duration of
|
|
28
|
+
* the call.
|
|
29
|
+
*/
|
|
30
|
+
function SecretsSection({ env }: { env: EnvironmentSummary }) {
|
|
31
|
+
const setEnvironmentSecret = useBuilderStore((s) => s.setEnvironmentSecret);
|
|
32
|
+
const deleteEnvironmentSecret = useBuilderStore((s) => s.deleteEnvironmentSecret);
|
|
33
|
+
const [key, setKey] = useState('');
|
|
34
|
+
const [value, setValue] = useState('');
|
|
35
|
+
const [pending, setPending] = useState<string | null>(null); // key being written/cleared
|
|
36
|
+
const [error, setError] = useState<string | null>(null);
|
|
37
|
+
|
|
38
|
+
const onSet = async () => {
|
|
39
|
+
const k = key.trim();
|
|
40
|
+
if (!k || !value) return;
|
|
41
|
+
setError(null);
|
|
42
|
+
setPending(k);
|
|
43
|
+
try {
|
|
44
|
+
await setEnvironmentSecret(env.name, k, value);
|
|
45
|
+
setKey('');
|
|
46
|
+
setValue('');
|
|
47
|
+
} catch (err) {
|
|
48
|
+
setError(err instanceof Error ? err.message : 'Failed to set secret');
|
|
49
|
+
} finally {
|
|
50
|
+
setPending(null);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const onClear = async (k: string) => {
|
|
55
|
+
setError(null);
|
|
56
|
+
setPending(k);
|
|
57
|
+
try {
|
|
58
|
+
await deleteEnvironmentSecret(env.name, k);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
setError(err instanceof Error ? err.message : 'Failed to clear secret');
|
|
61
|
+
} finally {
|
|
62
|
+
setPending(null);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="flex flex-col gap-2">
|
|
68
|
+
<SectionTitle>Secrets</SectionTitle>
|
|
69
|
+
{env.secretKeys.length === 0 && (
|
|
70
|
+
<div className="text-[11.5px] text-muted-foreground">No secrets stored.</div>
|
|
71
|
+
)}
|
|
72
|
+
{env.secretKeys.map((k) => (
|
|
73
|
+
<div key={k} className="flex items-center gap-2 text-[12px]">
|
|
74
|
+
<span className="min-w-0 truncate font-mono">{k}</span>
|
|
75
|
+
<span className="text-muted-foreground">••••••</span>
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
onClick={() => void onClear(k)}
|
|
79
|
+
disabled={pending === k}
|
|
80
|
+
title={`Clear ${k}`}
|
|
81
|
+
className="ml-auto cursor-pointer rounded-sm p-0.5 text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground disabled:opacity-50"
|
|
82
|
+
>
|
|
83
|
+
{pending === k ? (
|
|
84
|
+
<Loader2Icon className="size-3 animate-spin" />
|
|
85
|
+
) : (
|
|
86
|
+
<XIcon className="size-3" />
|
|
87
|
+
)}
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
))}
|
|
91
|
+
<div className="flex items-center gap-2">
|
|
92
|
+
<Input
|
|
93
|
+
value={key}
|
|
94
|
+
onChange={(e) => setKey(e.target.value)}
|
|
95
|
+
placeholder="key"
|
|
96
|
+
spellCheck={false}
|
|
97
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
98
|
+
/>
|
|
99
|
+
<Input
|
|
100
|
+
value={value}
|
|
101
|
+
onChange={(e) => setValue(e.target.value)}
|
|
102
|
+
placeholder="value"
|
|
103
|
+
type="password"
|
|
104
|
+
autoComplete="off"
|
|
105
|
+
className="h-7 flex-1 text-[12px]"
|
|
106
|
+
onKeyDown={(e) => {
|
|
107
|
+
if (e.key === 'Enter') void onSet();
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
<Button
|
|
111
|
+
variant="secondary"
|
|
112
|
+
size="sm"
|
|
113
|
+
className="h-7"
|
|
114
|
+
disabled={!key.trim() || !value || pending !== null}
|
|
115
|
+
onClick={() => void onSet()}
|
|
116
|
+
>
|
|
117
|
+
<PlusIcon className="size-3" />
|
|
118
|
+
Set
|
|
119
|
+
</Button>
|
|
120
|
+
</div>
|
|
121
|
+
<ErrorLine message={error} />
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Blank form state derived from an existing config (or sensible defaults). */
|
|
127
|
+
function formFromConfig(config: EnvAuth | undefined) {
|
|
128
|
+
return {
|
|
129
|
+
attachAs: config?.attach.as ?? ('cookie' as 'cookie' | 'header'),
|
|
130
|
+
attachName: config?.attach.name ?? '',
|
|
131
|
+
secretRef: config?.attach.secretRef ?? '',
|
|
132
|
+
prefix: config?.attach.prefix ?? '',
|
|
133
|
+
hasLogin: !!config?.login,
|
|
134
|
+
loginMethod: config?.login?.request.method ?? 'POST',
|
|
135
|
+
loginUrl: config?.login?.request.url ?? '',
|
|
136
|
+
loginHeaders: config?.login?.request.headers
|
|
137
|
+
? JSON.stringify(config.login.request.headers, null, 2)
|
|
138
|
+
: '',
|
|
139
|
+
bodyRef: config?.login?.request.bodyRef ?? '',
|
|
140
|
+
captureFrom: (config?.login?.capture.from ?? 'set-cookie') as 'set-cookie' | 'json' | 'header',
|
|
141
|
+
captureField:
|
|
142
|
+
config?.login?.capture.from === 'set-cookie'
|
|
143
|
+
? (config.login.capture.cookieName ?? '')
|
|
144
|
+
: config?.login?.capture.from === 'json'
|
|
145
|
+
? config.login.capture.path
|
|
146
|
+
: config?.login?.capture.from === 'header'
|
|
147
|
+
? config.login.capture.name
|
|
148
|
+
: '',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const fieldLabel = 'w-24 shrink-0 text-[11px] text-muted-foreground';
|
|
153
|
+
const fieldRow = 'flex items-center gap-2';
|
|
154
|
+
const selectCls =
|
|
155
|
+
'h-7 rounded-md border border-input bg-transparent px-2 text-[12px] outline-none focus-visible:ring-1 focus-visible:ring-ring';
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Auth configuration: maps 1:1 to the runner's EnvAuth shape. Values here are
|
|
159
|
+
* config (names/refs), never credentials — the one sharp edge is login
|
|
160
|
+
* headers, which round-trip through GET /environments, so we warn against
|
|
161
|
+
* putting secrets there.
|
|
162
|
+
*/
|
|
163
|
+
function AuthSection({ env }: { env: EnvironmentSummary }) {
|
|
164
|
+
const setEnvironmentAuth = useBuilderStore((s) => s.setEnvironmentAuth);
|
|
165
|
+
const loginEnvironment = useBuilderStore((s) => s.loginEnvironment);
|
|
166
|
+
const runAgent = useBuilderStore((s) => s.runAgent);
|
|
167
|
+
const configured = env.auth?.configured ?? false;
|
|
168
|
+
const [editing, setEditing] = useState(false);
|
|
169
|
+
const [aiOpen, setAiOpen] = useState(false);
|
|
170
|
+
const [aiInstruction, setAiInstruction] = useState('');
|
|
171
|
+
const [form, setForm] = useState(() => formFromConfig(env.auth?.config));
|
|
172
|
+
const [pending, setPending] = useState<'save' | 'clear' | 'login' | null>(null);
|
|
173
|
+
const [error, setError] = useState<string | null>(null);
|
|
174
|
+
const [loginResult, setLoginResult] = useState<string | null>(null);
|
|
175
|
+
|
|
176
|
+
const set = <K extends keyof typeof form>(k: K, v: (typeof form)[K]) =>
|
|
177
|
+
setForm((f) => ({ ...f, [k]: v }));
|
|
178
|
+
|
|
179
|
+
const buildAuth = (): EnvAuth | { error: string } => {
|
|
180
|
+
if (!form.attachName.trim()) return { error: 'Attach name is required' };
|
|
181
|
+
if (!form.secretRef.trim()) return { error: 'Secret ref is required' };
|
|
182
|
+
const auth: EnvAuth = {
|
|
183
|
+
attach: {
|
|
184
|
+
as: form.attachAs,
|
|
185
|
+
name: form.attachName.trim(),
|
|
186
|
+
secretRef: form.secretRef.trim(),
|
|
187
|
+
...(form.prefix ? { prefix: form.prefix } : {}),
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
if (form.hasLogin) {
|
|
191
|
+
if (!form.loginUrl.trim()) return { error: 'Login URL is required' };
|
|
192
|
+
let headers: Record<string, string> | undefined;
|
|
193
|
+
if (form.loginHeaders.trim()) {
|
|
194
|
+
try {
|
|
195
|
+
headers = JSON.parse(form.loginHeaders) as Record<string, string>;
|
|
196
|
+
} catch {
|
|
197
|
+
return { error: 'Login headers must be valid JSON' };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const field = form.captureField.trim();
|
|
201
|
+
auth.login = {
|
|
202
|
+
request: {
|
|
203
|
+
method: form.loginMethod.trim() || 'POST',
|
|
204
|
+
url: form.loginUrl.trim(),
|
|
205
|
+
...(headers ? { headers } : {}),
|
|
206
|
+
...(form.bodyRef.trim() ? { bodyRef: form.bodyRef.trim() } : {}),
|
|
207
|
+
},
|
|
208
|
+
capture:
|
|
209
|
+
form.captureFrom === 'set-cookie'
|
|
210
|
+
? { from: 'set-cookie', ...(field ? { cookieName: field } : {}) }
|
|
211
|
+
: form.captureFrom === 'json'
|
|
212
|
+
? { from: 'json', path: field }
|
|
213
|
+
: { from: 'header', name: field },
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return auth;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const onSave = async () => {
|
|
220
|
+
const auth = buildAuth();
|
|
221
|
+
if ('error' in auth) {
|
|
222
|
+
setError(auth.error);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
setError(null);
|
|
226
|
+
setPending('save');
|
|
227
|
+
try {
|
|
228
|
+
await setEnvironmentAuth(env.name, auth);
|
|
229
|
+
setEditing(false);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
setError(err instanceof Error ? err.message : 'Failed to save auth config');
|
|
232
|
+
} finally {
|
|
233
|
+
setPending(null);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const onClearAuth = async () => {
|
|
238
|
+
setError(null);
|
|
239
|
+
setPending('clear');
|
|
240
|
+
try {
|
|
241
|
+
await setEnvironmentAuth(env.name, null);
|
|
242
|
+
setEditing(false);
|
|
243
|
+
setForm(formFromConfig(undefined));
|
|
244
|
+
} catch (err) {
|
|
245
|
+
setError(err instanceof Error ? err.message : 'Failed to clear auth config');
|
|
246
|
+
} finally {
|
|
247
|
+
setPending(null);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const onLogin = async () => {
|
|
252
|
+
setError(null);
|
|
253
|
+
setLoginResult(null);
|
|
254
|
+
setPending('login');
|
|
255
|
+
try {
|
|
256
|
+
await loginEnvironment(env.name);
|
|
257
|
+
setLoginResult('Logged in.');
|
|
258
|
+
} catch (err) {
|
|
259
|
+
setError(err instanceof Error ? err.message : 'Login failed');
|
|
260
|
+
} finally {
|
|
261
|
+
setPending(null);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const onSetupWithAi = () => {
|
|
266
|
+
const instruction = aiInstruction.trim();
|
|
267
|
+
if (!instruction) return;
|
|
268
|
+
void runAgent({ action: 'setup-auth', environment: env.name, instruction });
|
|
269
|
+
setAiOpen(false);
|
|
270
|
+
setAiInstruction('');
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const summary = env.auth?.config
|
|
274
|
+
? `${env.auth.config.attach.as} · ${env.auth.config.attach.name} ← «${env.auth.config.attach.secretRef}»${env.auth.config.login ? ` · login ${env.auth.config.login.request.method} ${env.auth.config.login.request.url}` : ''}`
|
|
275
|
+
: null;
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<div className="flex flex-col gap-2">
|
|
279
|
+
<SectionTitle>Auth</SectionTitle>
|
|
280
|
+
|
|
281
|
+
{!configured && !editing && !aiOpen && (
|
|
282
|
+
<div className="flex flex-col gap-2">
|
|
283
|
+
<div className="text-[11.5px] text-muted-foreground">
|
|
284
|
+
No auth configured — runs against this environment carry no credential.
|
|
285
|
+
</div>
|
|
286
|
+
<div className="flex gap-2">
|
|
287
|
+
<Button variant="secondary" size="sm" className="h-7" onClick={() => setAiOpen(true)}>
|
|
288
|
+
<SparklesIcon className="size-3" />
|
|
289
|
+
Set up with AI
|
|
290
|
+
</Button>
|
|
291
|
+
<Button variant="ghost" size="sm" className="h-7" onClick={() => setEditing(true)}>
|
|
292
|
+
Configure manually
|
|
293
|
+
</Button>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
)}
|
|
297
|
+
|
|
298
|
+
{aiOpen && (
|
|
299
|
+
<div className="flex flex-col gap-2">
|
|
300
|
+
<textarea
|
|
301
|
+
value={aiInstruction}
|
|
302
|
+
onChange={(e) => setAiInstruction(e.target.value)}
|
|
303
|
+
placeholder={
|
|
304
|
+
'Describe how this API logs in — paste a curl, name the endpoint, say where the token/cookie comes back…'
|
|
305
|
+
}
|
|
306
|
+
rows={4}
|
|
307
|
+
className="w-full resize-none rounded-md border border-input bg-transparent px-2.5 py-2 text-[12px] outline-none placeholder:text-muted-foreground/60 focus-visible:ring-1 focus-visible:ring-ring"
|
|
308
|
+
/>
|
|
309
|
+
<div className="text-[11px] text-muted-foreground">
|
|
310
|
+
The agent writes the config and verifies the login. It never sees credential values —
|
|
311
|
+
you'll enter those here as secrets.
|
|
312
|
+
</div>
|
|
313
|
+
<div className="flex justify-end gap-2">
|
|
314
|
+
<Button variant="ghost" size="sm" className="h-7" onClick={() => setAiOpen(false)}>
|
|
315
|
+
Cancel
|
|
316
|
+
</Button>
|
|
317
|
+
<Button
|
|
318
|
+
variant="secondary"
|
|
319
|
+
size="sm"
|
|
320
|
+
className="h-7"
|
|
321
|
+
disabled={!aiInstruction.trim()}
|
|
322
|
+
onClick={onSetupWithAi}
|
|
323
|
+
>
|
|
324
|
+
<SparklesIcon className="size-3" />
|
|
325
|
+
Start
|
|
326
|
+
</Button>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
)}
|
|
330
|
+
|
|
331
|
+
{configured && !editing && (
|
|
332
|
+
<div className="flex flex-col gap-2">
|
|
333
|
+
<div className="flex items-center gap-2 text-[12px]">
|
|
334
|
+
<span
|
|
335
|
+
className={cn(
|
|
336
|
+
'size-1.5 shrink-0 rounded-full',
|
|
337
|
+
env.auth?.authenticated ? 'bg-success' : 'border border-muted-foreground',
|
|
338
|
+
)}
|
|
339
|
+
/>
|
|
340
|
+
<span className="min-w-0 truncate font-mono text-[11.5px]">{summary}</span>
|
|
341
|
+
</div>
|
|
342
|
+
<div className="flex gap-2">
|
|
343
|
+
<Button
|
|
344
|
+
variant="secondary"
|
|
345
|
+
size="sm"
|
|
346
|
+
className="h-7"
|
|
347
|
+
disabled={pending !== null}
|
|
348
|
+
onClick={() => void onLogin()}
|
|
349
|
+
>
|
|
350
|
+
{pending === 'login' && <Loader2Icon className="size-3 animate-spin" />}
|
|
351
|
+
{env.auth?.authenticated ? 'Re-log in' : 'Log in'}
|
|
352
|
+
</Button>
|
|
353
|
+
<Button
|
|
354
|
+
variant="ghost"
|
|
355
|
+
size="sm"
|
|
356
|
+
className="h-7"
|
|
357
|
+
onClick={() => {
|
|
358
|
+
setForm(formFromConfig(env.auth?.config));
|
|
359
|
+
setEditing(true);
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
Edit
|
|
363
|
+
</Button>
|
|
364
|
+
<Button
|
|
365
|
+
variant="ghost"
|
|
366
|
+
size="sm"
|
|
367
|
+
className="h-7 text-destructive hover:text-destructive"
|
|
368
|
+
disabled={pending !== null}
|
|
369
|
+
onClick={() => void onClearAuth()}
|
|
370
|
+
>
|
|
371
|
+
{pending === 'clear' && <Loader2Icon className="size-3 animate-spin" />}
|
|
372
|
+
Remove
|
|
373
|
+
</Button>
|
|
374
|
+
</div>
|
|
375
|
+
{loginResult && <div className="text-[11.5px] text-success">{loginResult}</div>}
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
378
|
+
|
|
379
|
+
{editing && (
|
|
380
|
+
<div className="flex flex-col gap-2">
|
|
381
|
+
<div className={fieldRow}>
|
|
382
|
+
<span className={fieldLabel}>Attach as</span>
|
|
383
|
+
<select
|
|
384
|
+
value={form.attachAs}
|
|
385
|
+
onChange={(e) => set('attachAs', e.target.value as 'cookie' | 'header')}
|
|
386
|
+
className={selectCls}
|
|
387
|
+
>
|
|
388
|
+
<option value="cookie">cookie</option>
|
|
389
|
+
<option value="header">header</option>
|
|
390
|
+
</select>
|
|
391
|
+
<Input
|
|
392
|
+
value={form.attachName}
|
|
393
|
+
onChange={(e) => set('attachName', e.target.value)}
|
|
394
|
+
placeholder={form.attachAs === 'cookie' ? 'cookie name' : 'header name'}
|
|
395
|
+
spellCheck={false}
|
|
396
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
397
|
+
/>
|
|
398
|
+
</div>
|
|
399
|
+
<div className={fieldRow}>
|
|
400
|
+
<span className={fieldLabel}>Secret ref</span>
|
|
401
|
+
<Input
|
|
402
|
+
value={form.secretRef}
|
|
403
|
+
onChange={(e) => set('secretRef', e.target.value)}
|
|
404
|
+
placeholder="secret key holding the credential"
|
|
405
|
+
spellCheck={false}
|
|
406
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
407
|
+
/>
|
|
408
|
+
</div>
|
|
409
|
+
<div className={fieldRow}>
|
|
410
|
+
<span className={fieldLabel}>Prefix</span>
|
|
411
|
+
<Input
|
|
412
|
+
value={form.prefix}
|
|
413
|
+
onChange={(e) => set('prefix', e.target.value)}
|
|
414
|
+
placeholder='optional, e.g. "Bearer "'
|
|
415
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
416
|
+
/>
|
|
417
|
+
</div>
|
|
418
|
+
|
|
419
|
+
<label className="flex cursor-pointer items-center gap-2 text-[12px]">
|
|
420
|
+
<input
|
|
421
|
+
type="checkbox"
|
|
422
|
+
checked={form.hasLogin}
|
|
423
|
+
onChange={(e) => set('hasLogin', e.target.checked)}
|
|
424
|
+
/>
|
|
425
|
+
Login request mints the credential
|
|
426
|
+
</label>
|
|
427
|
+
|
|
428
|
+
{form.hasLogin && (
|
|
429
|
+
<>
|
|
430
|
+
<div className={fieldRow}>
|
|
431
|
+
<span className={fieldLabel}>Login</span>
|
|
432
|
+
<Input
|
|
433
|
+
value={form.loginMethod}
|
|
434
|
+
onChange={(e) => set('loginMethod', e.target.value)}
|
|
435
|
+
className="h-7 w-20 font-mono text-[12px]"
|
|
436
|
+
spellCheck={false}
|
|
437
|
+
/>
|
|
438
|
+
<Input
|
|
439
|
+
value={form.loginUrl}
|
|
440
|
+
onChange={(e) => set('loginUrl', e.target.value)}
|
|
441
|
+
placeholder="https://…/login"
|
|
442
|
+
spellCheck={false}
|
|
443
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
444
|
+
/>
|
|
445
|
+
</div>
|
|
446
|
+
<div className={fieldRow}>
|
|
447
|
+
<span className={fieldLabel}>Body secret</span>
|
|
448
|
+
<Input
|
|
449
|
+
value={form.bodyRef}
|
|
450
|
+
onChange={(e) => set('bodyRef', e.target.value)}
|
|
451
|
+
placeholder="secret key holding the login body JSON"
|
|
452
|
+
spellCheck={false}
|
|
453
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
454
|
+
/>
|
|
455
|
+
</div>
|
|
456
|
+
<div className="flex items-start gap-2">
|
|
457
|
+
<span className={cn(fieldLabel, 'pt-1.5')}>Headers</span>
|
|
458
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
459
|
+
<textarea
|
|
460
|
+
value={form.loginHeaders}
|
|
461
|
+
onChange={(e) => set('loginHeaders', e.target.value)}
|
|
462
|
+
placeholder='{"origin": "http://localhost:5173"}'
|
|
463
|
+
rows={2}
|
|
464
|
+
spellCheck={false}
|
|
465
|
+
className="w-full resize-none rounded-md border border-input bg-transparent px-2.5 py-1.5 font-mono text-[11.5px] outline-none placeholder:text-muted-foreground/50 focus-visible:ring-1 focus-visible:ring-ring"
|
|
466
|
+
/>
|
|
467
|
+
<div className="text-[10.5px] text-muted-foreground">
|
|
468
|
+
Config only — never put credentials here; use the body secret instead.
|
|
469
|
+
</div>
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
<div className={fieldRow}>
|
|
473
|
+
<span className={fieldLabel}>Capture</span>
|
|
474
|
+
<select
|
|
475
|
+
value={form.captureFrom}
|
|
476
|
+
onChange={(e) =>
|
|
477
|
+
set('captureFrom', e.target.value as 'set-cookie' | 'json' | 'header')
|
|
478
|
+
}
|
|
479
|
+
className={selectCls}
|
|
480
|
+
>
|
|
481
|
+
<option value="set-cookie">set-cookie</option>
|
|
482
|
+
<option value="json">json path</option>
|
|
483
|
+
<option value="header">header</option>
|
|
484
|
+
</select>
|
|
485
|
+
<Input
|
|
486
|
+
value={form.captureField}
|
|
487
|
+
onChange={(e) => set('captureField', e.target.value)}
|
|
488
|
+
placeholder={
|
|
489
|
+
form.captureFrom === 'set-cookie'
|
|
490
|
+
? 'cookie name (optional)'
|
|
491
|
+
: form.captureFrom === 'json'
|
|
492
|
+
? 'dot.path.to.token'
|
|
493
|
+
: 'header name'
|
|
494
|
+
}
|
|
495
|
+
spellCheck={false}
|
|
496
|
+
className="h-7 flex-1 font-mono text-[12px]"
|
|
497
|
+
/>
|
|
498
|
+
</div>
|
|
499
|
+
</>
|
|
500
|
+
)}
|
|
501
|
+
|
|
502
|
+
<div className="flex justify-end gap-2">
|
|
503
|
+
<Button variant="ghost" size="sm" className="h-7" onClick={() => setEditing(false)}>
|
|
504
|
+
Cancel
|
|
505
|
+
</Button>
|
|
506
|
+
<Button
|
|
507
|
+
variant="secondary"
|
|
508
|
+
size="sm"
|
|
509
|
+
className="h-7"
|
|
510
|
+
disabled={pending !== null}
|
|
511
|
+
onClick={() => void onSave()}
|
|
512
|
+
>
|
|
513
|
+
{pending === 'save' && <Loader2Icon className="size-3 animate-spin" />}
|
|
514
|
+
Save
|
|
515
|
+
</Button>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
)}
|
|
519
|
+
|
|
520
|
+
<ErrorLine message={error} />
|
|
521
|
+
</div>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Manage Environment dialog: secrets (write-only), auth config, and login —
|
|
527
|
+
* everything that previously required hand-editing emberflow.environments.json.
|
|
528
|
+
*/
|
|
529
|
+
export function EnvironmentDialog({
|
|
530
|
+
env,
|
|
531
|
+
open,
|
|
532
|
+
onOpenChange,
|
|
533
|
+
}: {
|
|
534
|
+
env: EnvironmentSummary | null;
|
|
535
|
+
open: boolean;
|
|
536
|
+
onOpenChange: (open: boolean) => void;
|
|
537
|
+
}) {
|
|
538
|
+
if (!env) return null;
|
|
539
|
+
return (
|
|
540
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
541
|
+
<DialogContent className="max-w-lg">
|
|
542
|
+
<DialogTitle className="flex items-center gap-2">
|
|
543
|
+
{env.name}
|
|
544
|
+
{env.protected && (
|
|
545
|
+
<span className="flex items-center gap-1 text-[10.5px] font-normal uppercase tracking-wide text-destructive-foreground/80">
|
|
546
|
+
<ShieldIcon className="size-3" /> protected
|
|
547
|
+
</span>
|
|
548
|
+
)}
|
|
549
|
+
{env.auth?.configured && (
|
|
550
|
+
<span
|
|
551
|
+
className={cn(
|
|
552
|
+
'ml-auto size-1.5 rounded-full',
|
|
553
|
+
env.auth.authenticated ? 'bg-success' : 'border border-muted-foreground',
|
|
554
|
+
)}
|
|
555
|
+
title={env.auth.authenticated ? 'Authenticated' : 'Not authenticated'}
|
|
556
|
+
/>
|
|
557
|
+
)}
|
|
558
|
+
</DialogTitle>
|
|
559
|
+
<DialogDescription className="sr-only">
|
|
560
|
+
Manage secrets and auth for the {env.name} environment.
|
|
561
|
+
</DialogDescription>
|
|
562
|
+
<div className="flex flex-col gap-5">
|
|
563
|
+
<SecretsSection env={env} />
|
|
564
|
+
<AuthSection env={env} />
|
|
565
|
+
</div>
|
|
566
|
+
</DialogContent>
|
|
567
|
+
</Dialog>
|
|
568
|
+
);
|
|
569
|
+
}
|