machinaos 0.0.76 → 0.0.78
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 +143 -107
- package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
- package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
- package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
- package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
- package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
- package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
- package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
- package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
- package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
- package/client/dist/assets/index-DQ0nwhec.js +257 -0
- package/client/dist/assets/index-DxmbVskS.css +1 -0
- package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
- package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
- package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
- package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
- package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
- package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
- package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
- package/client/dist/index.html +37 -3
- package/client/index.html +28 -1
- package/client/package.json +44 -40
- package/client/src/App.tsx +2 -0
- package/client/src/Dashboard.tsx +157 -45
- package/client/src/ParameterPanel.tsx +3 -5
- package/client/src/adapters/nodeSpecToDescription.ts +1 -0
- package/client/src/assets/icons/NodeIcon.tsx +32 -0
- package/client/src/assets/icons/index.ts +4 -0
- package/client/src/assets/icons/stripe.svg +1 -0
- package/client/src/assets/icons/themedGlyphs.ts +404 -0
- package/client/src/components/AIAgentNode.tsx +77 -53
- package/client/src/components/GenericNode.tsx +34 -52
- package/client/src/components/OutputPanel.tsx +64 -147
- package/client/src/components/ParameterRenderer.tsx +5 -3
- package/client/src/components/SkillEditorModal.tsx +9 -18
- package/client/src/components/SquareNode.tsx +97 -115
- package/client/src/components/StartNode.tsx +32 -42
- package/client/src/components/SvgFilterDefs.tsx +54 -0
- package/client/src/components/TeamMonitorNode.tsx +12 -14
- package/client/src/components/ToolkitNode.tsx +35 -60
- package/client/src/components/TriggerNode.tsx +43 -77
- package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
- package/client/src/components/credentials/CredentialsModal.tsx +98 -30
- package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
- package/client/src/components/credentials/catalogueAdapter.ts +17 -1
- package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
- package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
- package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
- package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
- package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
- package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
- package/client/src/components/credentials/types.ts +12 -2
- package/client/src/components/credentials/useCredentialPanel.ts +43 -19
- package/client/src/components/icons/AIProviderIcons.tsx +16 -0
- package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
- package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
- package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
- package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
- package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
- package/client/src/components/parameterPanel/InputSection.tsx +9 -7
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
- package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
- package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
- package/client/src/components/ui/AIResultModal.tsx +1 -1
- package/client/src/components/ui/CollapsibleSection.tsx +9 -5
- package/client/src/components/ui/CommandPalette.tsx +147 -0
- package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
- package/client/src/components/ui/ComponentItem.tsx +13 -7
- package/client/src/components/ui/ComponentPalette.tsx +24 -13
- package/client/src/components/ui/ConsolePanel.tsx +19 -11
- package/client/src/components/ui/DropCap.tsx +28 -0
- package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
- package/client/src/components/ui/InputNodesPanel.tsx +1 -1
- package/client/src/components/ui/Modal.tsx +38 -6
- package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
- package/client/src/components/ui/SettingsPanel.tsx +42 -13
- package/client/src/components/ui/StatusBar.tsx +108 -0
- package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
- package/client/src/components/ui/TopToolbar.tsx +42 -25
- package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
- package/client/src/components/ui/action-button.tsx +40 -15
- package/client/src/components/ui/button.tsx +24 -1
- package/client/src/components/ui/dropdown-menu.tsx +24 -2
- package/client/src/components/ui/input.tsx +19 -2
- package/client/src/components/ui/select.tsx +15 -0
- package/client/src/components/ui/textarea.tsx +15 -2
- package/client/src/contexts/AuthContext.tsx +148 -109
- package/client/src/contexts/ThemeContext.tsx +93 -17
- package/client/src/contexts/WebSocketContext.tsx +373 -206
- package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
- package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
- package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
- package/client/src/hooks/useAppTheme.ts +209 -7
- package/client/src/hooks/useAutoSkillEdges.ts +7 -2
- package/client/src/hooks/useCatalogueQuery.ts +67 -1
- package/client/src/hooks/useDragVariable.ts +1 -1
- package/client/src/hooks/useNodeAllowlist.ts +115 -8
- package/client/src/hooks/useOnboarding.ts +20 -8
- package/client/src/hooks/useParameterPanel.ts +2 -1
- package/client/src/hooks/useReactFlowNodes.ts +2 -1
- package/client/src/hooks/useSound.ts +185 -0
- package/client/src/hooks/useWorkflowManagement.ts +6 -8
- package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
- package/client/src/index.css +65 -3
- package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
- package/client/src/lib/aiModelProviders.ts +8 -0
- package/client/src/lib/connectionConfig.ts +107 -0
- package/client/src/lib/queryPersist.ts +13 -5
- package/client/src/lib/sound.ts +393 -0
- package/client/src/main.tsx +20 -0
- package/client/src/store/useAppStore.ts +26 -0
- package/client/src/styles/canvasAnimations.ts +37 -36
- package/client/src/styles/theme.ts +36 -20
- package/client/src/test/setup.ts +1 -0
- package/client/src/themes/atomic.css +253 -0
- package/client/src/themes/base.css +373 -0
- package/client/src/themes/cyber.css +890 -0
- package/client/src/themes/dark.css +70 -0
- package/client/src/themes/edo.css +246 -0
- package/client/src/themes/greek.css +293 -0
- package/client/src/themes/light.css +78 -0
- package/client/src/themes/plague.css +253 -0
- package/client/src/themes/renaissance.css +727 -0
- package/client/src/themes/rot.css +249 -0
- package/client/src/themes/steampunk.css +272 -0
- package/client/src/themes/surveillance.css +289 -0
- package/client/src/themes/wasteland.css +250 -0
- package/client/src/types/INodeProperties.ts +5 -0
- package/client/src/types/NodeTypes.ts +11 -1
- package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
- package/client/src/types/cloudEvents.ts +78 -0
- package/client/src/vite-env.d.ts +7 -0
- package/client/tsconfig.json +1 -1
- package/client/vite.config.js +62 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/machina/commands/build.py +51 -7
- package/machina/pyproject.toml +4 -0
- package/machina/supervisor.py +12 -2
- package/machina/tree.py +71 -21
- package/package.json +4 -4
- package/scripts/install.js +16 -1
- package/server/config/ai_cli_providers.json +54 -0
- package/server/config/credential_providers.json +109 -2
- package/server/config/llm_defaults.json +24 -0
- package/server/config/model_registry.json +338 -499
- package/server/config/node_allowlist.json +16 -1
- package/server/config/pricing.json +8 -0
- package/server/constants.py +38 -15
- package/server/core/container.py +2 -2
- package/server/core/credentials_database.py +35 -2
- package/server/core/logging.py +4 -3
- package/server/main.py +99 -13
- package/server/models/node_metadata.py +1 -0
- package/server/nodejs/package.json +8 -6
- package/server/nodejs/src/index.ts +22 -5
- package/server/nodes/README.md +31 -4
- package/server/nodes/agent/_inline.py +2 -0
- package/server/nodes/agent/_specialized.py +6 -3
- package/server/nodes/agent/ai_agent.py +13 -3
- package/server/nodes/agent/chat_agent.py +6 -3
- package/server/nodes/agent/claude_code_agent.py +287 -75
- package/server/nodes/agent/codex_agent.py +239 -0
- package/server/nodes/agent/deep_agent.py +3 -3
- package/server/nodes/agent/rlm_agent.py +3 -3
- package/server/nodes/android/__init__.py +31 -1
- package/server/nodes/android/_base.py +9 -5
- package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
- package/server/nodes/android/_handlers.py +154 -0
- package/server/nodes/android/_option_loaders.py +44 -0
- package/server/nodes/android/_refresh.py +127 -0
- package/server/{services/android → nodes/android/_relay}/client.py +4 -4
- package/server/{routers/android.py → nodes/android/_router.py} +27 -8
- package/server/nodes/browser/browser.py +2 -2
- package/server/nodes/code/_base.py +6 -2
- package/server/nodes/code/_claude_code.py +134 -0
- package/server/nodes/document/embedding_generator.py +3 -3
- package/server/nodes/document/http_scraper.py +3 -3
- package/server/nodes/document/vector_store.py +5 -5
- package/server/nodes/email/__init__.py +11 -1
- package/server/nodes/email/_filters.py +21 -0
- package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
- package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
- package/server/nodes/email/email_read.py +1 -1
- package/server/nodes/email/email_receive.py +54 -5
- package/server/nodes/email/email_send.py +1 -1
- package/server/nodes/filesystem/shell.py +24 -1
- package/server/nodes/google/__init__.py +55 -1
- package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
- package/server/nodes/google/_base.py +2 -2
- package/server/nodes/google/_credentials.py +5 -5
- package/server/nodes/google/_filters.py +25 -0
- package/server/nodes/google/_handlers.py +57 -0
- package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
- package/server/nodes/google/_option_loaders.py +107 -0
- package/server/nodes/google/_refresh.py +66 -0
- package/server/nodes/google/_router.py +131 -0
- package/server/nodes/google/gmail_receive.py +41 -4
- package/server/nodes/groups.py +1 -0
- package/server/nodes/location/_credentials.py +45 -1
- package/server/{services/maps.py → nodes/location/_service.py} +18 -3
- package/server/nodes/location/gmaps_create.py +4 -4
- package/server/nodes/location/gmaps_locations.py +4 -4
- package/server/nodes/location/gmaps_nearby_places.py +4 -4
- package/server/nodes/model/_base.py +8 -3
- package/server/nodes/model/_credentials.py +96 -8
- package/server/nodes/model/_local_validator.py +345 -0
- package/server/nodes/model/lmstudio_chat_model.py +23 -0
- package/server/nodes/model/ollama_chat_model.py +25 -0
- package/server/nodes/proxy/_usage.py +2 -2
- package/server/nodes/proxy/proxy_config.py +14 -14
- package/server/nodes/proxy/proxy_request.py +4 -4
- package/server/nodes/scraper/_credentials.py +29 -1
- package/server/nodes/scraper/apify_actor.py +9 -9
- package/server/nodes/scraper/crawlee_scraper.py +5 -5
- package/server/nodes/search/brave_search.py +4 -0
- package/server/nodes/search/perplexity_search.py +9 -0
- package/server/nodes/search/serper_search.py +3 -0
- package/server/nodes/skill/simple_memory.py +12 -0
- package/server/nodes/social/_base.py +2 -2
- package/server/nodes/stripe/__init__.py +46 -0
- package/server/nodes/stripe/_credentials.py +33 -0
- package/server/nodes/stripe/_handlers.py +270 -0
- package/server/nodes/stripe/_install.py +127 -0
- package/server/nodes/stripe/_source.py +174 -0
- package/server/nodes/stripe/stripe_action.py +81 -0
- package/server/nodes/stripe/stripe_receive.py +92 -0
- package/server/nodes/telegram/_credentials.py +52 -1
- package/server/nodes/telegram/_handlers.py +19 -18
- package/server/nodes/telegram/_service.py +134 -32
- package/server/nodes/telegram/telegram_send.py +5 -6
- package/server/nodes/text/file_handler.py +2 -2
- package/server/nodes/text/text_generator.py +2 -2
- package/server/nodes/tool/agent_builder.py +630 -0
- package/server/nodes/tool/task_manager.py +144 -2
- package/server/nodes/twitter/__init__.py +38 -1
- package/server/nodes/twitter/_base.py +7 -7
- package/server/nodes/twitter/_credentials.py +1 -1
- package/server/nodes/twitter/_filters.py +37 -0
- package/server/nodes/twitter/_handlers.py +77 -0
- package/server/nodes/twitter/_oauth.py +124 -0
- package/server/nodes/twitter/_refresh.py +78 -0
- package/server/nodes/twitter/_router.py +29 -0
- package/server/nodes/twitter/twitter_receive.py +4 -0
- package/server/nodes/visuals.json +64 -19
- package/server/nodes/whatsapp/__init__.py +45 -5
- package/server/nodes/whatsapp/_base.py +3 -3
- package/server/nodes/whatsapp/_filters.py +137 -0
- package/server/nodes/whatsapp/_handlers.py +167 -0
- package/server/nodes/whatsapp/_option_loaders.py +68 -0
- package/server/nodes/whatsapp/_refresh.py +62 -0
- package/server/nodes/whatsapp/_runtime.py +1 -1
- package/server/pyproject.toml +29 -7
- package/server/routers/schemas.py +2 -2
- package/server/routers/webhook.py +26 -9
- package/server/routers/websocket.py +149 -810
- package/server/services/ai.py +89 -8
- package/server/services/auth.py +220 -43
- package/server/services/claude_oauth.py +126 -100
- package/server/services/cli_agent/__init__.py +78 -0
- package/server/services/cli_agent/_handlers.py +237 -0
- package/server/services/cli_agent/config.py +112 -0
- package/server/services/cli_agent/factory.py +48 -0
- package/server/services/cli_agent/lockfile.py +141 -0
- package/server/services/cli_agent/mcp_server.py +482 -0
- package/server/services/cli_agent/protocol.py +173 -0
- package/server/services/cli_agent/providers/__init__.py +9 -0
- package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
- package/server/services/cli_agent/providers/google_gemini.py +80 -0
- package/server/services/cli_agent/providers/openai_codex.py +310 -0
- package/server/services/cli_agent/service.py +607 -0
- package/server/services/cli_agent/session.py +618 -0
- package/server/services/cli_agent/types.py +227 -0
- package/server/services/cli_agent/workflow_tools.py +233 -0
- package/server/services/credential_registry.py +26 -1
- package/server/services/deployment/manager.py +26 -145
- package/server/services/deployment/poll_registry.py +59 -0
- package/server/services/event_waiter.py +76 -246
- package/server/services/events/__init__.py +54 -0
- package/server/services/events/cli.py +78 -0
- package/server/services/events/daemon.py +163 -0
- package/server/services/events/envelope.py +281 -0
- package/server/services/events/lifecycle.py +99 -0
- package/server/services/events/oauth_lifecycle.py +534 -0
- package/server/services/events/polling.py +60 -0
- package/server/services/events/push.py +36 -0
- package/server/services/events/source.py +63 -0
- package/server/services/events/triggers.py +118 -0
- package/server/services/events/verifiers/__init__.py +25 -0
- package/server/services/events/verifiers/base.py +28 -0
- package/server/services/events/verifiers/github.py +25 -0
- package/server/services/events/verifiers/hmac_basic.py +32 -0
- package/server/services/events/verifiers/standard_webhooks.py +47 -0
- package/server/services/events/verifiers/stripe.py +42 -0
- package/server/services/events/webhook.py +105 -0
- package/server/services/handlers/tools.py +28 -186
- package/server/services/llm/config.py +7 -0
- package/server/services/llm/factory.py +8 -2
- package/server/services/memory/__init__.py +52 -0
- package/server/services/memory/jsonl.py +80 -0
- package/server/services/memory/markdown.py +65 -0
- package/server/services/memory/state.py +112 -0
- package/server/services/memory/vector_store.py +40 -0
- package/server/services/model_registry.py +76 -0
- package/server/services/node_allowlist.py +71 -15
- package/server/services/node_executor.py +2 -2
- package/server/services/node_output_schemas.py +21 -10
- package/server/services/node_spec.py +1 -1
- package/server/services/oauth_utils.py +1 -1
- package/server/services/plugin/__init__.py +2 -0
- package/server/services/plugin/base.py +44 -2
- package/server/services/plugin/credential.py +288 -1
- package/server/services/plugin/deps.py +105 -0
- package/server/services/plugin/edge_walker.py +12 -4
- package/server/services/plugin/oauth.py +381 -0
- package/server/services/plugin/polling.py +247 -0
- package/server/services/plugin/registry.py +145 -0
- package/server/services/plugin/singleton.py +65 -0
- package/server/services/plugin/ws.py +81 -0
- package/server/services/process_service.py +31 -2
- package/server/services/status_broadcaster.py +155 -238
- package/server/services/temporal/workflow.py +7 -7
- package/server/services/workflow.py +21 -3
- package/server/services/ws_handler_registry.py +111 -28
- package/server/skills/GUIDE.md +16 -1
- package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
- package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
- package/server/tests/credentials/test_auth_service.py +16 -9
- package/server/tests/credentials/test_credential_broadcasts.py +219 -0
- package/server/tests/credentials/test_google_oauth.py +6 -6
- package/server/tests/credentials/test_oauth_utils.py +1 -1
- package/server/tests/credentials/test_twitter_oauth.py +2 -2
- package/server/tests/credentials/test_websocket_handlers.py +44 -20
- package/server/tests/llm/test_factory.py +1 -0
- package/server/tests/llm/test_wiring.py +5 -1
- package/server/tests/nodes/_compat.py +24 -24
- package/server/tests/nodes/test_agent_builder.py +439 -0
- package/server/tests/nodes/test_ai_tools.py +18 -14
- package/server/tests/nodes/test_code_fs_process.py +17 -8
- package/server/tests/nodes/test_email.py +10 -9
- package/server/tests/nodes/test_google_workspace.py +2 -2
- package/server/tests/nodes/test_specialized_agents.py +100 -53
- package/server/tests/nodes/test_stripe_plugin.py +293 -0
- package/server/tests/nodes/test_telegram_social.py +4 -4
- package/server/tests/nodes/test_twitter.py +1 -1
- package/server/tests/nodes/test_web_automation.py +2 -2
- package/server/tests/nodes/test_whatsapp.py +9 -9
- package/server/tests/services/cli_agent/__init__.py +0 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
- package/server/tests/services/cli_agent/test_providers.py +358 -0
- package/server/tests/services/cli_agent/test_service.py +298 -0
- package/server/tests/services/memory/__init__.py +0 -0
- package/server/tests/services/memory/test_jsonl.py +188 -0
- package/server/tests/services/test_events.py +333 -0
- package/server/tests/test_node_spec.py +56 -16
- package/server/tests/test_plugin_helpers.py +116 -0
- package/server/tests/test_plugin_self_containment.py +486 -0
- package/server/tests/test_status_broadcasts.py +425 -0
- package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
- package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
- package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
- package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
- package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
- package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
- package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
- package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
- package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
- package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
- package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
- package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
- package/client/dist/assets/index-D9tZfgvi.js +0 -363
- package/client/dist/assets/index-al7snTkG.css +0 -1
- package/client/src/components/credentials/providers.tsx +0 -177
- package/server/routers/google.py +0 -277
- package/server/routers/maps.py +0 -142
- package/server/routers/twitter.py +0 -365
- package/server/services/claude_code_service.py +0 -106
- package/server/services/memory.py +0 -159
- package/server/services/node_option_loaders/__init__.py +0 -77
- package/server/services/node_option_loaders/android_loaders.py +0 -55
- package/server/services/node_option_loaders/google_loaders.py +0 -97
- package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
- package/server/services/twitter_oauth.py +0 -411
- package/server/services/websocket_client.py +0 -29
- /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
- /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
- /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
- /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
package/client/src/index.css
CHANGED
|
@@ -116,26 +116,32 @@
|
|
|
116
116
|
* convention). */
|
|
117
117
|
--action-run: 135 94% 65%; /* primary CTA: deploy / run */
|
|
118
118
|
--action-run-soft: 135 94% 65% / 0.15;
|
|
119
|
+
--action-run-hover: 135 94% 65% / 0.25;
|
|
119
120
|
--action-run-border: 135 94% 65% / 0.6;
|
|
120
121
|
|
|
121
122
|
--action-stop: 326 100% 74%; /* destructive: stop / cancel / logout */
|
|
122
123
|
--action-stop-soft: 326 100% 74% / 0.15;
|
|
124
|
+
--action-stop-hover: 326 100% 74% / 0.25;
|
|
123
125
|
--action-stop-border: 326 100% 74% / 0.6;
|
|
124
126
|
|
|
125
127
|
--action-save: 191 97% 77%; /* commit / save / panel toggle */
|
|
126
128
|
--action-save-soft: 191 97% 77% / 0.15;
|
|
129
|
+
--action-save-hover: 191 97% 77% / 0.25;
|
|
127
130
|
--action-save-border: 191 97% 77% / 0.6;
|
|
128
131
|
|
|
129
132
|
--action-config: 27 100% 71%; /* settings / config / warning */
|
|
130
133
|
--action-config-soft: 27 100% 71% / 0.15;
|
|
134
|
+
--action-config-hover: 27 100% 71% / 0.25;
|
|
131
135
|
--action-config-border: 27 100% 71% / 0.6;
|
|
132
136
|
|
|
133
137
|
--action-secret: 65 92% 76%; /* credentials / theme toggle */
|
|
134
138
|
--action-secret-soft: 65 92% 76% / 0.15;
|
|
139
|
+
--action-secret-hover: 65 92% 76% / 0.25;
|
|
135
140
|
--action-secret-border: 65 92% 76% / 0.6;
|
|
136
141
|
|
|
137
142
|
--action-tools: 265 89% 78%; /* component palette / creative */
|
|
138
143
|
--action-tools-soft: 265 89% 78% / 0.15;
|
|
144
|
+
--action-tools-hover: 265 89% 78% / 0.25;
|
|
139
145
|
--action-tools-border: 265 89% 78% / 0.6;
|
|
140
146
|
}
|
|
141
147
|
|
|
@@ -265,21 +271,27 @@
|
|
|
265
271
|
* the standard "soft tinted button" look (see ActionButton CVA). */
|
|
266
272
|
--color-action-run: hsl(var(--action-run));
|
|
267
273
|
--color-action-run-soft: hsl(var(--action-run-soft));
|
|
274
|
+
--color-action-run-hover: hsl(var(--action-run-hover));
|
|
268
275
|
--color-action-run-border: hsl(var(--action-run-border));
|
|
269
276
|
--color-action-stop: hsl(var(--action-stop));
|
|
270
277
|
--color-action-stop-soft: hsl(var(--action-stop-soft));
|
|
278
|
+
--color-action-stop-hover: hsl(var(--action-stop-hover));
|
|
271
279
|
--color-action-stop-border: hsl(var(--action-stop-border));
|
|
272
280
|
--color-action-save: hsl(var(--action-save));
|
|
273
281
|
--color-action-save-soft: hsl(var(--action-save-soft));
|
|
282
|
+
--color-action-save-hover: hsl(var(--action-save-hover));
|
|
274
283
|
--color-action-save-border: hsl(var(--action-save-border));
|
|
275
284
|
--color-action-config: hsl(var(--action-config));
|
|
276
285
|
--color-action-config-soft: hsl(var(--action-config-soft));
|
|
286
|
+
--color-action-config-hover: hsl(var(--action-config-hover));
|
|
277
287
|
--color-action-config-border: hsl(var(--action-config-border));
|
|
278
288
|
--color-action-secret: hsl(var(--action-secret));
|
|
279
289
|
--color-action-secret-soft: hsl(var(--action-secret-soft));
|
|
290
|
+
--color-action-secret-hover: hsl(var(--action-secret-hover));
|
|
280
291
|
--color-action-secret-border: hsl(var(--action-secret-border));
|
|
281
292
|
--color-action-tools: hsl(var(--action-tools));
|
|
282
293
|
--color-action-tools-soft: hsl(var(--action-tools-soft));
|
|
294
|
+
--color-action-tools-hover: hsl(var(--action-tools-hover));
|
|
283
295
|
--color-action-tools-border: hsl(var(--action-tools-border));
|
|
284
296
|
|
|
285
297
|
--radius-sm: calc(var(--radius) - 4px);
|
|
@@ -289,15 +301,65 @@
|
|
|
289
301
|
|
|
290
302
|
--font-sans: 'Geist Variable', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
291
303
|
--font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, monospace;
|
|
304
|
+
|
|
305
|
+
/* ── New-contract tokens (design_handoff_machinaos_themes) ──────────
|
|
306
|
+
* Surface / foreground / semantic / typography / radius tokens
|
|
307
|
+
* exposed as Tailwind utilities. Defined per-theme in client/src/
|
|
308
|
+
* themes/{light,dark,renaissance,cyber}.css — themes redefine the
|
|
309
|
+
* `--bg-app`, `--fg-default`, etc. CSS variables under
|
|
310
|
+
* `:root[data-theme="..."]`; these `@theme inline` aliases give
|
|
311
|
+
* Tailwind classes that resolve against whichever theme is active.
|
|
312
|
+
*
|
|
313
|
+
* Note: full-colour values (not HSL triplets), so `bg-bg-app/50`
|
|
314
|
+
* alpha composition does NOT work — use `--bg-overlay` or the
|
|
315
|
+
* shadcn alias (`bg-background/50`) when you need alpha. */
|
|
316
|
+
--color-bg-app: var(--bg-app);
|
|
317
|
+
--color-bg-panel: var(--bg-panel);
|
|
318
|
+
--color-bg-canvas: var(--bg-canvas);
|
|
319
|
+
--color-bg-elevated: var(--bg-elevated);
|
|
320
|
+
--color-bg-input: var(--bg-input);
|
|
321
|
+
--color-bg-hover: var(--bg-hover);
|
|
322
|
+
--color-bg-active: var(--bg-active);
|
|
323
|
+
--color-bg-overlay: var(--bg-overlay);
|
|
324
|
+
|
|
325
|
+
--color-fg-default: var(--fg-default);
|
|
326
|
+
--color-fg-muted: var(--fg-muted);
|
|
327
|
+
--color-fg-faint: var(--fg-faint);
|
|
328
|
+
--color-fg-on-accent: var(--fg-on-accent);
|
|
329
|
+
--color-fg-on-success: var(--fg-on-success);
|
|
330
|
+
--color-fg-on-danger: var(--fg-on-danger);
|
|
331
|
+
--color-fg-on-warning: var(--fg-on-warning);
|
|
332
|
+
|
|
333
|
+
/* Border tokens — exposed as Tailwind colour utilities so call sites
|
|
334
|
+
* can write `border-default`, `border-strong`, `border-focus` /
|
|
335
|
+
* `bg-border-default` etc. */
|
|
336
|
+
--color-border-default: var(--border-default);
|
|
337
|
+
--color-border-strong: var(--border-strong);
|
|
338
|
+
--color-border-focus: var(--border-focus);
|
|
339
|
+
|
|
340
|
+
/* Typography family aliases — `font-display`, `font-body` (font-mono
|
|
341
|
+
* is already exposed above; we re-declare it here so it tracks the
|
|
342
|
+
* per-theme `--font-mono` instead of the static fallback). */
|
|
343
|
+
--font-display: var(--font-display);
|
|
344
|
+
--font-body: var(--font-body);
|
|
345
|
+
|
|
346
|
+
--radius-pill: var(--radius-pill);
|
|
292
347
|
}
|
|
293
348
|
|
|
294
349
|
/* ============================================================================
|
|
295
350
|
* React Flow chrome — light + dark
|
|
296
351
|
* ============================================================================ */
|
|
297
352
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
353
|
+
/* `.react-flow` is intentionally transparent so the parent
|
|
354
|
+
* `.canvas-host` / `.canvas` `background-image` (per-theme
|
|
355
|
+
* `--canvas-grid` + multi-layer gradient stack) paints through.
|
|
356
|
+
* Per the upstream v11 stylesheet, `.react-flow` ships with only
|
|
357
|
+
* `direction: ltr;` — no background. Painting one here would hide
|
|
358
|
+
* the per-theme decorations (Cyber perspective grid, Renaissance
|
|
359
|
+
* fleur-de-lis, Surveillance CCTV reticle, etc.). When migrating
|
|
360
|
+
* to @xyflow/react v12, set `--xy-background-color: transparent;`
|
|
361
|
+
* on `.xy-flow` (v12 sets a default opaque background via custom
|
|
362
|
+
* property). */
|
|
301
363
|
|
|
302
364
|
.react-flow__background pattern circle {
|
|
303
365
|
fill: hsl(var(--muted-foreground) / 0.3) !important;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock-in tests for `lib/connectionConfig.ts`.
|
|
3
|
+
*
|
|
4
|
+
* The tuning constants here drive AuthContext's TanStack Query retry
|
|
5
|
+
* policy and PartySocket's reconnect envelope. Drift in either —
|
|
6
|
+
* accidentally raising AUTH_RETRY.CAP_MS to 30s, or flipping
|
|
7
|
+
* WS_CLOSE.NORMAL_CLOSURE off RFC 6455's 1000 — would silently
|
|
8
|
+
* regress perceived launch time or break the "intentional close"
|
|
9
|
+
* contract.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { AUTH_RETRY, WS_CLOSE, WS_RECONNECT } from '../connectionConfig';
|
|
14
|
+
|
|
15
|
+
describe('AUTH_RETRY', () => {
|
|
16
|
+
it('keeps base under 100 ms for sub-second first retries', () => {
|
|
17
|
+
// The previous hand-rolled chain started at 1000 ms, which cost
|
|
18
|
+
// ~3 s of perceived launch time when the backend was ready 100 ms
|
|
19
|
+
// after the first failure. Anything above 100 ms here would
|
|
20
|
+
// re-introduce that stall.
|
|
21
|
+
expect(AUTH_RETRY.BASE_MS).toBeLessThan(100);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('caps individual retries below 5 s so a slow backend still recovers in <30 s wall', () => {
|
|
25
|
+
expect(AUTH_RETRY.CAP_MS).toBeGreaterThan(0);
|
|
26
|
+
expect(AUTH_RETRY.CAP_MS).toBeLessThan(5_000);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('allows enough attempts to span a multi-second backend cold start', () => {
|
|
30
|
+
// With BASE_MS=50, CAP_MS=4000: cumulative upper bound
|
|
31
|
+
// sum_{n=0..N-1} min(CAP_MS, BASE_MS * 2^n)
|
|
32
|
+
// For N=7 → ~10 s, which covers the typical 4 s backend window.
|
|
33
|
+
expect(AUTH_RETRY.MAX_ATTEMPTS).toBeGreaterThanOrEqual(5);
|
|
34
|
+
expect(AUTH_RETRY.MAX_ATTEMPTS).toBeLessThanOrEqual(10);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('full-jitter formula stays within the documented envelope for every attempt', () => {
|
|
38
|
+
// The actual formula:
|
|
39
|
+
// sleep = random(0, min(CAP_MS, BASE_MS * 2 ** attempt))
|
|
40
|
+
// Sample 1000 draws per attempt and confirm every draw stays under
|
|
41
|
+
// the cap. (Random.random() is bounded [0, 1) so the upper bound is
|
|
42
|
+
// strict.)
|
|
43
|
+
for (let attempt = 0; attempt < AUTH_RETRY.MAX_ATTEMPTS; attempt += 1) {
|
|
44
|
+
const upperBound = Math.min(AUTH_RETRY.CAP_MS, AUTH_RETRY.BASE_MS * 2 ** attempt);
|
|
45
|
+
for (let i = 0; i < 100; i += 1) {
|
|
46
|
+
const draw = Math.random() * upperBound;
|
|
47
|
+
expect(draw).toBeGreaterThanOrEqual(0);
|
|
48
|
+
expect(draw).toBeLessThanOrEqual(AUTH_RETRY.CAP_MS);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('WS_RECONNECT', () => {
|
|
55
|
+
it('first reconnect attempt is sub-second', () => {
|
|
56
|
+
// Matches the AuthContext retry shape so first reconnect after a
|
|
57
|
+
// transient drop is fast.
|
|
58
|
+
expect(WS_RECONNECT.MIN_DELAY_MS).toBeGreaterThan(0);
|
|
59
|
+
expect(WS_RECONNECT.MIN_DELAY_MS).toBeLessThan(1_000);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('reconnect cap is bounded so dev-iteration restarts are visible', () => {
|
|
63
|
+
expect(WS_RECONNECT.MAX_DELAY_MS).toBeGreaterThan(WS_RECONNECT.MIN_DELAY_MS);
|
|
64
|
+
expect(WS_RECONNECT.MAX_DELAY_MS).toBeLessThanOrEqual(15_000);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('grow factor is between 1.0 and 2.0 (gentle ramp, not aggressive)', () => {
|
|
68
|
+
expect(WS_RECONNECT.GROW_FACTOR).toBeGreaterThan(1.0);
|
|
69
|
+
expect(WS_RECONNECT.GROW_FACTOR).toBeLessThan(2.0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('enqueue cap is positive and finite', () => {
|
|
73
|
+
expect(WS_RECONNECT.MAX_ENQUEUED_MESSAGES).toBeGreaterThan(0);
|
|
74
|
+
expect(Number.isFinite(WS_RECONNECT.MAX_ENQUEUED_MESSAGES)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('WS_CLOSE', () => {
|
|
79
|
+
it('NORMAL_CLOSURE matches RFC 6455 §7.4.1', () => {
|
|
80
|
+
// PartySocket inspects this exact value to skip its reconnect
|
|
81
|
+
// loop. Any drift would either re-trigger reconnect on logout
|
|
82
|
+
// (1000 → !1000) or suppress reconnect on real drops (drift the
|
|
83
|
+
// other way). RFC 6455 §7.4.1 fixes this at 1000.
|
|
84
|
+
expect(WS_CLOSE.NORMAL_CLOSURE).toBe(1000);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('NORMAL_CLOSURE is in the 1000-1014 reserved-server range', () => {
|
|
88
|
+
expect(WS_CLOSE.NORMAL_CLOSURE).toBeGreaterThanOrEqual(1000);
|
|
89
|
+
expect(WS_CLOSE.NORMAL_CLOSURE).toBeLessThanOrEqual(1014);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -18,6 +18,14 @@ const AI_MODEL_PROVIDERS = [
|
|
|
18
18
|
'deepseek',
|
|
19
19
|
'kimi',
|
|
20
20
|
'mistral',
|
|
21
|
+
// Local-server providers — the chat-model plugins exist
|
|
22
|
+
// (`ollamaChatModel`, `lmstudioChatModel`), so they MUST live in this
|
|
23
|
+
// map for `ParameterRenderer` to derive the provider id from the node
|
|
24
|
+
// type when the schema-implicit `provider` param is absent. Without
|
|
25
|
+
// them the model dropdown stays empty and the runtime falls back to
|
|
26
|
+
// the OpenAI cloud.
|
|
27
|
+
'ollama',
|
|
28
|
+
'lmstudio',
|
|
21
29
|
] as const;
|
|
22
30
|
|
|
23
31
|
/** `<provider>ChatModel` node type → provider id (matches the backend-served
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralised tuning constants for the auth + WebSocket connection layer.
|
|
3
|
+
*
|
|
4
|
+
* The hand-rolled retry chain in `AuthContext` and the flat 3-second
|
|
5
|
+
* reconnect timer in `WebSocketContext` were retired in favour of
|
|
6
|
+
* library-backed retry policies (TanStack Query for auth, PartySocket
|
|
7
|
+
* for the WS). The numeric envelope of those policies lives here so a
|
|
8
|
+
* future tuning pass — tighter backoff, longer cap, smaller queue — is
|
|
9
|
+
* a single-file edit and tests can reference the same values.
|
|
10
|
+
*
|
|
11
|
+
* Pattern mirrors `client/src/lib/queryConfig.ts`'s `STALE_TIME` /
|
|
12
|
+
* `GC_TIME` buckets: named, frozen, JSDoc-explained.
|
|
13
|
+
*
|
|
14
|
+
* Pre-existing constants (REQUEST_TIMEOUT, ping interval) stay in
|
|
15
|
+
* `WebSocketContext.tsx` for now — moving them is out of scope for
|
|
16
|
+
* this change.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Auth-bootstrap retry tuning, consumed by `AuthContext`'s
|
|
21
|
+
* `useQuery({ retry, retryDelay })` config.
|
|
22
|
+
*
|
|
23
|
+
* Full-jitter exponential backoff per the AWS Architecture Blog —
|
|
24
|
+
* https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/.
|
|
25
|
+
*
|
|
26
|
+
* Formula:
|
|
27
|
+
*
|
|
28
|
+
* sleep = random_between(0, min(CAP_MS, BASE_MS * 2^attempt))
|
|
29
|
+
*
|
|
30
|
+
* Sample sequence (BASE_MS=50, CAP_MS=4000):
|
|
31
|
+
*
|
|
32
|
+
* attempt 0 → up to 50ms
|
|
33
|
+
* attempt 1 → up to 100ms
|
|
34
|
+
* attempt 2 → up to 200ms
|
|
35
|
+
* attempt 3 → up to 400ms
|
|
36
|
+
* attempt 4 → up to 800ms
|
|
37
|
+
* attempt 5 → up to 1600ms
|
|
38
|
+
* attempt 6 → up to 3200ms
|
|
39
|
+
* attempt 7 → up to 4000ms (capped)
|
|
40
|
+
*
|
|
41
|
+
* Total upper-bound wall time ≈ 10 s across 7 retries — covers the
|
|
42
|
+
* typical ~4 s backend cold-start window in 4-5 attempts. The previous
|
|
43
|
+
* `1000ms · Math.pow(2, n)` chain needed 31 s in the worst case.
|
|
44
|
+
*/
|
|
45
|
+
export const AUTH_RETRY = {
|
|
46
|
+
/** Base for the exponential factor (ms). */
|
|
47
|
+
BASE_MS: 50,
|
|
48
|
+
/** Cap on a single retry's delay (ms). */
|
|
49
|
+
CAP_MS: 4_000,
|
|
50
|
+
/** Stop retrying after this many failed attempts. */
|
|
51
|
+
MAX_ATTEMPTS: 7,
|
|
52
|
+
} as const;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* WebSocket reconnect envelope, consumed by `WebSocketContext`'s
|
|
56
|
+
* `new ReconnectingWebSocket(url, [], {...})` constructor.
|
|
57
|
+
*
|
|
58
|
+
* `partysocket/ws` interleaves jitter automatically; these values
|
|
59
|
+
* define the envelope it stays inside.
|
|
60
|
+
*
|
|
61
|
+
* Sample reconnect sequence (MIN_DELAY_MS=250, GROW_FACTOR=1.3,
|
|
62
|
+
* MAX_DELAY_MS=8000):
|
|
63
|
+
*
|
|
64
|
+
* attempt 1 → ~250ms
|
|
65
|
+
* attempt 2 → ~325ms
|
|
66
|
+
* attempt 3 → ~422ms
|
|
67
|
+
* ...
|
|
68
|
+
* attempt N → capped at 8s
|
|
69
|
+
*
|
|
70
|
+
* Refs:
|
|
71
|
+
* https://docs.partykit.io/reference/partysocket-api/
|
|
72
|
+
* https://github.com/cloudflare/partykit/tree/main/packages/partysocket
|
|
73
|
+
*/
|
|
74
|
+
export const WS_RECONNECT = {
|
|
75
|
+
/** Delay before the first reconnect attempt (ms). */
|
|
76
|
+
MIN_DELAY_MS: 250,
|
|
77
|
+
/** Cap on any single reconnect delay (ms). */
|
|
78
|
+
MAX_DELAY_MS: 8_000,
|
|
79
|
+
/** Multiplier applied to the previous delay each attempt. */
|
|
80
|
+
GROW_FACTOR: 1.3,
|
|
81
|
+
/**
|
|
82
|
+
* Bound on the send-while-disconnected buffer. PartySocket replays
|
|
83
|
+
* these calls on the next OPEN. Mirrors the previous
|
|
84
|
+
* `pendingSendQueueRef` cap intent.
|
|
85
|
+
*/
|
|
86
|
+
MAX_ENQUEUED_MESSAGES: 200,
|
|
87
|
+
} as const;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* WebSocket close codes per RFC 6455 §7.4.1.
|
|
91
|
+
* https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1
|
|
92
|
+
*
|
|
93
|
+
* Browsers expose these via `CloseEvent.code`. PartySocket inspects
|
|
94
|
+
* `1000` to skip its reconnect loop (intentional close); every other
|
|
95
|
+
* code is treated as transient and triggers reconnect.
|
|
96
|
+
*
|
|
97
|
+
* Only the subset this app actively sends or branches on is listed.
|
|
98
|
+
* Add new entries here rather than inlining the numeric literal.
|
|
99
|
+
*/
|
|
100
|
+
export const WS_CLOSE = {
|
|
101
|
+
/**
|
|
102
|
+
* 1000 — Normal Closure. The connection successfully completed the
|
|
103
|
+
* purpose for which it was created. Used for logout + unmount
|
|
104
|
+
* teardown so PartySocket does not reconnect.
|
|
105
|
+
*/
|
|
106
|
+
NORMAL_CLOSURE: 1000,
|
|
107
|
+
} as const;
|
|
@@ -62,11 +62,19 @@ export const queryPersistMaxAge = 24 * 60 * 60 * 1000;
|
|
|
62
62
|
const PERSISTED_KEY_PREFIXES = [
|
|
63
63
|
'nodeSpec',
|
|
64
64
|
'nodeGroups',
|
|
65
|
-
// credentialValues
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
65
|
+
// credentialValues was previously persisted here for the cred-panel
|
|
66
|
+
// form-field warm-start. Removed per OWASP HTML5 Security Cheat Sheet
|
|
67
|
+
// / ASVS V9.9: a credentialValues entry contains the decrypted API
|
|
68
|
+
// key value (populated by getStoredApiKey on the WS roundtrip), and
|
|
69
|
+
// localStorage stores it in plaintext readable via DevTools on shared
|
|
70
|
+
// / compromised machines. The in-memory TanStack Query cache (still
|
|
71
|
+
// gcTime: FOREVER, see queryClient.ts) keeps the form populated for
|
|
72
|
+
// the session lifetime; on reload the panel refetches — one
|
|
73
|
+
// roundtrip cost, fine because the modal only opens on user action.
|
|
74
|
+
//
|
|
75
|
+
// credentialCatalogue is also NOT here -- it has its own IDB
|
|
76
|
+
// warm-start in useCatalogueQuery.ts (idb-keyval). skillContent
|
|
77
|
+
// stays in-memory only.
|
|
70
78
|
];
|
|
71
79
|
|
|
72
80
|
export function shouldPersistQuery(query: Query): boolean {
|