From 2e1ccb16f5049308911e3c632333e2f3c6444b3c Mon Sep 17 00:00:00 2001 From: Waleed Date: Sat, 27 Dec 2025 15:15:43 -0800 Subject: [PATCH 1/5] improvement(ui): hide divider when following subblock value is null (#2608) * improvement(ui): hide divider when following subblock value is null * closed gap in mcp dynamic args subblock --- apps/sim/app/_styles/globals.css | 11 +++++ .../mcp-dynamic-args/mcp-dynamic-args.tsx | 48 ++++++++++--------- .../editor/components/sub-block/sub-block.tsx | 2 +- .../panel/components/editor/editor.tsx | 4 +- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/apps/sim/app/_styles/globals.css b/apps/sim/app/_styles/globals.css index 013e6b1d45..96a17260c5 100644 --- a/apps/sim/app/_styles/globals.css +++ b/apps/sim/app/_styles/globals.css @@ -391,6 +391,17 @@ color: var(--text-primary); } +/** + * Subblock divider visibility + * Hides dividers when adjacent subblocks render empty content (e.g., schedule-info without data). + * Uses CSS :has() to detect empty .subblock-content elements and hide associated dividers. + * Selectors ordered by ascending specificity: (0,4,0) then (0,5,0) + */ +.subblock-row:has(> .subblock-content:empty) > .subblock-divider, +.subblock-row:has(+ .subblock-row > .subblock-content:empty) > .subblock-divider { + display: none; +} + /** * Dark mode specific overrides */ diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx index 62630afc5a..787248134f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx @@ -537,7 +537,7 @@ export function McpDynamicArgs({ } return ( -
+
{/* Hidden dummy inputs to prevent browser password manager autofill */} - {toolSchema.properties && - Object.entries(toolSchema.properties).map(([paramName, paramSchema]) => { - const inputType = getInputType(paramSchema as any) - const showLabel = inputType !== 'switch' - - return ( -
- {showLabel && ( - - )} - {renderParameterInput(paramName, paramSchema as any)} -
- ) - })} +
+ {toolSchema.properties && + Object.entries(toolSchema.properties).map(([paramName, paramSchema]) => { + const inputType = getInputType(paramSchema as any) + const showLabel = inputType !== 'switch' + + return ( +
+ {showLabel && ( + + )} + {renderParameterInput(paramName, paramSchema as any)} +
+ ) + })} +
) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index 3954dac453..310c3df0dd 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -866,7 +866,7 @@ function SubBlockComponent({ } return ( -
+
{renderLabel( config, isValidJson, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx index 017f9f91ce..181732e956 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx @@ -341,7 +341,7 @@ export function Editor() { ) return ( -
+
{index < subBlocks.length - 1 && ( -
+
Date: Sat, 27 Dec 2025 15:18:03 -0800 Subject: [PATCH 2/5] improvement: required permissions, oauth modal badge (#2609) --- .../components/oauth-required-modal.tsx | 14 ++++++++++--- .../credential-selector.tsx | 21 ++++++++++++++----- .../components/tool-credential-selector.tsx | 21 ++++++++++++++----- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx index c06a12ea2b..37bb13bbe7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx @@ -3,7 +3,15 @@ import { useMemo } from 'react' import { createLogger } from '@sim/logger' import { Check } from 'lucide-react' -import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@/components/emcn' +import { + Badge, + Button, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, +} from '@/components/emcn' import { client } from '@/lib/auth/auth-client' import { getProviderIdFromServiceId, @@ -407,9 +415,9 @@ export function OAuthRequiredModal({
{getScopeDescription(scope)} {newScopesSet.has(scope) && ( - + New - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx index d24cd4ed8d..d902200dd2 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx @@ -228,7 +228,7 @@ export function CredentialSelector({ ) return ( - <> +
{needsUpdate && ( -
- Additional permissions required - {!isForeign && } +
+
+ + Additional permissions required +
+ {!isForeign && ( + + )}
)} @@ -264,7 +275,7 @@ export function CredentialSelector({ serviceId={serviceId} /> )} - +
) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx index f7f755ac30..761935d7d3 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector.tsx @@ -200,7 +200,7 @@ export function ToolCredentialSelector({ ) return ( - <> +
{needsUpdate && ( -
- Additional permissions required - {!isForeign && } +
+
+ + Additional permissions required +
+ {!isForeign && ( + + )}
)} @@ -234,7 +245,7 @@ export function ToolCredentialSelector({ serviceId={serviceId} /> )} - +
) } From aa9cc5604aa15a17105825504614a63becbc10a3 Mon Sep 17 00:00:00 2001 From: Waleed Date: Sat, 27 Dec 2025 15:23:02 -0800 Subject: [PATCH 3/5] improvement(usage-indicator): update query invalidation for usage to update in realtime (#2607) * improvement(usage-indicator): update query invalidation for usage to update in realtime * ack PR comments --- .../w/[workflowId]/hooks/use-wand.ts | 2 +- .../hooks/use-workflow-execution.ts | 4 +-- .../cancel-subscription.tsx | 2 +- .../usage-indicator/usage-indicator.tsx | 2 +- apps/sim/hooks/queries/subscription.ts | 26 ++++++++++++------- apps/sim/stores/panel/copilot/store.ts | 2 +- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand.ts index 2f6d9bc47b..e1ed0500a2 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand.ts @@ -252,7 +252,7 @@ export function useWand({ }) setTimeout(() => { - queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) + queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) }, 1000) } catch (error: any) { if (error.name === 'AbortError') { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts index f2d9cc2f76..61af9f0624 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts @@ -573,7 +573,7 @@ export function useWorkflowExecution() { // Invalidate subscription queries to update usage setTimeout(() => { - queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) + queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) }, 1000) safeEnqueue(encodeSSE({ event: 'final', data: result })) @@ -646,7 +646,7 @@ export function useWorkflowExecution() { // Invalidate subscription queries to update usage setTimeout(() => { - queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) + queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) }, 1000) } return result diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx index c778f06603..2461f3e739 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx @@ -165,7 +165,7 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub logger.info('Subscription restored successfully', result) } - await queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) + await queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) if (activeOrgId) { await queryClient.invalidateQueries({ queryKey: organizationKeys.detail(activeOrgId) }) await queryClient.invalidateQueries({ queryKey: organizationKeys.billing(activeOrgId) }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/usage-indicator/usage-indicator.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/usage-indicator/usage-indicator.tsx index dd1df673ae..97f4dc8c36 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/usage-indicator/usage-indicator.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/usage-indicator/usage-indicator.tsx @@ -199,7 +199,7 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) { useEffect(() => { const handleOperationConfirmed = () => { setTimeout(() => { - queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) + queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) }, 1000) } onOperationConfirmed(handleOperationConfirmed) diff --git a/apps/sim/hooks/queries/subscription.ts b/apps/sim/hooks/queries/subscription.ts index 750d5f5251..89ded91231 100644 --- a/apps/sim/hooks/queries/subscription.ts +++ b/apps/sim/hooks/queries/subscription.ts @@ -98,13 +98,13 @@ export function useUpdateUsageLimit() { return response.json() }, onMutate: async ({ limit }) => { - await queryClient.cancelQueries({ queryKey: subscriptionKeys.user() }) - await queryClient.cancelQueries({ queryKey: subscriptionKeys.usage() }) + await queryClient.cancelQueries({ queryKey: subscriptionKeys.all }) - const previousSubscriptionData = queryClient.getQueryData(subscriptionKeys.user()) + const previousSubscriptionData = queryClient.getQueryData(subscriptionKeys.user(false)) + const previousSubscriptionDataWithOrg = queryClient.getQueryData(subscriptionKeys.user(true)) const previousUsageData = queryClient.getQueryData(subscriptionKeys.usage()) - queryClient.setQueryData(subscriptionKeys.user(), (old: any) => { + const updateSubscriptionData = (old: any) => { if (!old) return old const currentUsage = old.data?.usage?.current || 0 const newPercentUsed = limit > 0 ? (currentUsage / limit) * 100 : 0 @@ -120,7 +120,10 @@ export function useUpdateUsageLimit() { }, }, } - }) + } + + queryClient.setQueryData(subscriptionKeys.user(false), updateSubscriptionData) + queryClient.setQueryData(subscriptionKeys.user(true), updateSubscriptionData) queryClient.setQueryData(subscriptionKeys.usage(), (old: any) => { if (!old) return old @@ -133,19 +136,24 @@ export function useUpdateUsageLimit() { } }) - return { previousSubscriptionData, previousUsageData } + return { previousSubscriptionData, previousSubscriptionDataWithOrg, previousUsageData } }, onError: (_err, _variables, context) => { if (context?.previousSubscriptionData) { - queryClient.setQueryData(subscriptionKeys.user(), context.previousSubscriptionData) + queryClient.setQueryData(subscriptionKeys.user(false), context.previousSubscriptionData) + } + if (context?.previousSubscriptionDataWithOrg) { + queryClient.setQueryData( + subscriptionKeys.user(true), + context.previousSubscriptionDataWithOrg + ) } if (context?.previousUsageData) { queryClient.setQueryData(subscriptionKeys.usage(), context.previousUsageData) } }, onSettled: () => { - queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) - queryClient.invalidateQueries({ queryKey: subscriptionKeys.usage() }) + queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) }, }) } diff --git a/apps/sim/stores/panel/copilot/store.ts b/apps/sim/stores/panel/copilot/store.ts index f963983ce0..8a5a634af2 100644 --- a/apps/sim/stores/panel/copilot/store.ts +++ b/apps/sim/stores/panel/copilot/store.ts @@ -2661,7 +2661,7 @@ export const useCopilotStore = create()( // Invalidate subscription queries to update usage setTimeout(() => { const queryClient = getQueryClient() - queryClient.invalidateQueries({ queryKey: subscriptionKeys.user() }) + queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }) }, 1000) } finally { clearTimeout(timeoutId) From cdc1a832d70ac20876162f196286917b280ff825 Mon Sep 17 00:00:00 2001 From: Waleed Date: Sat, 27 Dec 2025 17:55:16 -0800 Subject: [PATCH 4/5] fix(docker): add logger package to realtime dockerfile (#2610) --- docker/realtime.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/realtime.Dockerfile b/docker/realtime.Dockerfile index b1f9d4c9fe..863e403695 100644 --- a/docker/realtime.Dockerfile +++ b/docker/realtime.Dockerfile @@ -62,6 +62,9 @@ COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules # Copy db package (needed by socket) COPY --from=builder --chown=nextjs:nodejs /app/packages/db ./packages/db +# Copy logger package (workspace dependency used by socket) +COPY --from=builder --chown=nextjs:nodejs /app/packages/logger ./packages/logger + # Copy sim app (changes most frequently - placed last) COPY --from=builder --chown=nextjs:nodejs /app/apps/sim ./apps/sim From 7c0a3c15acac4394a6efe85d0bea9239fb55a86c Mon Sep 17 00:00:00 2001 From: Waleed Date: Sat, 27 Dec 2025 18:09:29 -0800 Subject: [PATCH 5/5] improvement(build): migrate to blacksmith sticky disks for faster builds, other build improvements (#2611) --- .github/workflows/test-build.yml | 19 ++++++++-------- docker/app.Dockerfile | 38 ++++++++++++++------------------ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 90e0ef524a..cd9f480b68 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -23,16 +23,17 @@ jobs: with: node-version: latest - - name: Cache Bun dependencies - uses: actions/cache@v4 + - name: Mount Bun cache (Sticky Disk) + uses: useblacksmith/stickydisk@v1 with: - path: | - ~/.bun/install/cache - node_modules - **/node_modules - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- + key: ${{ github.repository }}-bun-cache + path: ~/.bun/install/cache + + - name: Mount node_modules (Sticky Disk) + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-node-modules + path: ./node_modules - name: Install dependencies run: bun install --frozen-lockfile diff --git a/docker/app.Dockerfile b/docker/app.Dockerfile index 92f1c81292..d4faea4fac 100644 --- a/docker/app.Dockerfile +++ b/docker/app.Dockerfile @@ -1,21 +1,22 @@ # ======================================== -# Base Stage: Debian-based Bun +# Base Stage: Debian-based Bun with Node.js 22 # ======================================== FROM oven/bun:1.3.3-slim AS base +# Install Node.js 22 and common dependencies once in base stage +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && apt-get install -y --no-install-recommends \ + python3 python3-pip python3-venv make g++ curl ca-certificates bash ffmpeg \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs + # ======================================== # Dependencies Stage: Install Dependencies # ======================================== FROM base AS deps WORKDIR /app -# Install Node.js 22 for isolated-vm compilation (requires node-gyp and V8) -RUN apt-get update && apt-get install -y --no-install-recommends \ - python3 make g++ curl ca-certificates \ - && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ - && apt-get install -y nodejs \ - && rm -rf /var/lib/apt/lists/* - COPY package.json bun.lock turbo.json ./ RUN mkdir -p apps packages/db packages/testing packages/logger COPY apps/sim/package.json ./apps/sim/package.json @@ -25,6 +26,7 @@ COPY packages/logger/package.json ./packages/logger/package.json # Install turbo globally, then dependencies, then rebuild isolated-vm for Node.js RUN --mount=type=cache,id=bun-cache,target=/root/.bun/install/cache \ + --mount=type=cache,id=npm-cache,target=/root/.npm \ bun install -g turbo && \ HUSKY=0 bun install --omit=dev --ignore-scripts && \ cd $(readlink -f node_modules/isolated-vm) && npx node-gyp rebuild --release && cd /app @@ -89,13 +91,7 @@ RUN bun run build FROM base AS runner WORKDIR /app -# Install Node.js 22 (for isolated-vm worker), Python, and other runtime dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - python3 python3-pip python3-venv bash ffmpeg curl ca-certificates \ - && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ - && apt-get install -y nodejs \ - && rm -rf /var/lib/apt/lists/* - +# Node.js 22, Python, ffmpeg, etc. are already installed in base stage ENV NODE_ENV=production # Create non-root user and group @@ -113,15 +109,15 @@ COPY --from=deps --chown=nextjs:nodejs /app/node_modules/isolated-vm ./node_modu # Copy the isolated-vm worker script COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/execution/isolated-vm-worker.cjs ./apps/sim/lib/execution/isolated-vm-worker.cjs -# Guardrails setup (files need to be owned by nextjs for runtime) -COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/guardrails/setup.sh ./apps/sim/lib/guardrails/setup.sh +# Guardrails setup with pip caching COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/guardrails/requirements.txt ./apps/sim/lib/guardrails/requirements.txt COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/guardrails/validate_pii.py ./apps/sim/lib/guardrails/validate_pii.py -# Run guardrails setup as root, then fix ownership of generated venv files -RUN chmod +x ./apps/sim/lib/guardrails/setup.sh && \ - cd ./apps/sim/lib/guardrails && \ - ./setup.sh && \ +# Install Python dependencies with pip cache mount for faster rebuilds +RUN --mount=type=cache,target=/root/.cache/pip \ + python3 -m venv ./apps/sim/lib/guardrails/venv && \ + ./apps/sim/lib/guardrails/venv/bin/pip install --upgrade pip && \ + ./apps/sim/lib/guardrails/venv/bin/pip install -r ./apps/sim/lib/guardrails/requirements.txt && \ chown -R nextjs:nodejs /app/apps/sim/lib/guardrails # Create .next/cache directory with correct ownership