Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@
.git
.vscode
.idea
.claude

# external dependencies
node_modules
**/node_modules

# docker files
docker-compose*.yml
**/Dockerfile*

# build artifacts
dist/
**/dist
coverage/
**/coverage

# not needed files
README.md
tools/
!tools/deployment/nginx
.gitignore
.env
coverage/

# env files hold secrets — never in a build context
**/.env
**/.env.*
!**/.env.example
101 changes: 101 additions & 0 deletions .github/workflows/deploy-ai-studio.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Deploy AI Studio

on:
workflow_dispatch:
inputs:
image_tag:
description: 'Image tag (defaults to short SHA)'
required: false

permissions:
id-token: write
contents: read

env:
REGISTRY: synergycodes.azurecr.io
APP: wb-ai-studio

jobs:
build-and-push:
runs-on: ubuntu-latest
outputs:
image_tag: ${{ steps.tag.outputs.value }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Resolve image tag
id: tag
run: |
TAG="${{ inputs.image_tag || github.sha }}"
echo "value=${TAG::7}" >> "$GITHUB_OUTPUT"

- name: Log in to Azure
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Log in to ACR
run: az acr login --name synergycodes

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push runtime image
uses: docker/build-push-action@v6
with:
context: .
file: deploy/ai-studio/Dockerfile
target: runtime
push: true
tags: |
${{ env.REGISTRY }}/${{ env.APP }}:${{ steps.tag.outputs.value }}-runtime
${{ env.REGISTRY }}/${{ env.APP }}:latest-runtime
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.APP }}:latest-runtime
cache-to: type=inline

- name: Build and push web image
uses: docker/build-push-action@v6
with:
context: .
file: deploy/ai-studio/Dockerfile
target: web
push: true
tags: |
${{ env.REGISTRY }}/${{ env.APP }}:${{ steps.tag.outputs.value }}-web
${{ env.REGISTRY }}/${{ env.APP }}:latest-web
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.APP }}:latest-web
cache-to: type=inline
build-args: |
VITE_BACKEND_URL=

deploy:
runs-on: ubuntu-latest
needs: build-and-push

steps:
- name: Log in to Azure
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Refresh docker compose on Azure VM
run: |
OUTPUT=$(az vm run-command invoke \
--name ${{ vars.AI_STUDIO_VM_NAME }} \
--resource-group ${{ vars.AI_STUDIO_VM_RG }} \
--command-id RunShellScript \
--scripts '
set -e
az acr login --name synergycodes
docker compose -f /app/ai-studio/docker-compose.yml pull
docker compose -f /app/ai-studio/docker-compose.yml up -d --no-build --force-recreate
echo DEPLOY_SCRIPT_SUCCEEDED
')
echo "$OUTPUT"
echo "$OUTPUT" | grep -q DEPLOY_SCRIPT_SUCCEEDED
12 changes: 9 additions & 3 deletions .github/workflows/release-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ jobs:
# No `registry-url:` here on purpose. setup-node's registry-url writes
# an .npmrc with `_authToken=${NODE_AUTH_TOKEN}` and exports
# NODE_AUTH_TOKEN as the literal sentinel `XXXXX-XXXXX-XXXXX-XXXXX`.
# pnpm then sends that sentinel as a bearer token and skips OIDC
# entirely, so the publish PUT comes back as 404.
# npm/pnpm then send that sentinel as a bearer token and skip OIDC
# entirely, so the publish PUT comes back as 404. Node 24 ships npm
# 11.x which performs the Trusted Publisher OIDC exchange natively
# when no token is configured.
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24

- name: Enable Corepack
run: npm i -g corepack@latest
Expand Down Expand Up @@ -89,6 +91,10 @@ jobs:
# Authenticates via npm Trusted Publisher (OIDC). No NPM_TOKEN.
# Requires the package to have GitHub Actions trusted publishing
# configured on npmjs.com pointing at this workflow file.
# Uses `pnpm publish` (never plain `npm publish`) so that pnpm-only
# protocols in packages/sdk/package.json — `catalog:` for shared
# versions, `workspace:*` for sibling packages — are resolved into
# real version specifiers before the tarball is produced.
if: steps.check-version.outputs.already_published == 'false'
run: pnpm --filter @workflowbuilder/sdk publish --no-git-checks --access public --provenance

Expand Down
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Three onboarding paths (A installs from npm; B, C run the repo locally). README
| `pnpm preflight` | B/C | Verify Node / pnpm / Docker / ports / `.env` files. Add `--json` for agents |
| `pnpm dev` / `pnpm dev:demo` | B | Demo (UI only, port 4200). No backend, no Docker |
| `pnpm infra:up` | C | Start Postgres + Temporal in Docker. Required before backend/worker |
| `pnpm -F backend db:migrate` | C | Apply Drizzle migrations. First run, or after schema changes |
| `pnpm -F backend db:migrate` | C | Apply Drizzle migrations out-of-band (backend also auto-migrates on boot) |
| `pnpm dev:ai-studio` | C | Full stack: infra + backend (3001) + worker + AI Studio frontend (4201) |
| `pnpm dev:backend` | C | Backend only (debug). Needs infra up |
| `pnpm dev:worker` | C | Execution worker only (debug). Needs infra up |
Expand All @@ -22,7 +22,7 @@ Three onboarding paths (A installs from npm; B, C run the repo locally). README
| `pnpm test` | - | Run tests in `packages/sdk` and `packages/execution-core` |
| `pnpm check` | - | Lint + typecheck + format + knip |

Path B is UI-only and does not need Docker. Path C requires `pnpm infra:up` before backend/worker can start, and `db:migrate` on the first run.
Path B is UI-only and does not need Docker. Path C requires `pnpm infra:up` before backend/worker can start; the backend applies pending migrations automatically at boot.

### Agent signals

Expand All @@ -42,6 +42,9 @@ Long-running processes already emit stable log lines that scripts and agents can

```
tools/ - Root dev scripts: preflight, setup:env, infra wait
deployment/ - Swarm/Ansible deploy path mirroring the workflow-builder repo (ACR, Traefik)
deploy/
ai-studio/ - Production deployment: Dockerfile (runtime/web), compose, nginx, README
apps/
demo/ - Reference app consuming the SDK (React + Vite, port 4200)
ai-studio/ - Reference AI workflow product (React + Vite, port 4201)
Expand Down Expand Up @@ -137,7 +140,7 @@ The SDK is the only npm-published workspace; everything else under `apps/` and `
**Daily SDK change:**

1. Edit `packages/sdk/**`, run tests/typecheck locally.
2. **Add a changeset** with `/wb.changeset <patch|minor|major> "<summary>"`. Required for any consumer-visible change. Skip only for changes that don't ship in `dist/` (e.g. `eslint.config.mjs`, internal tests, source-only comments).
2. **Add a changeset** with `/wb.changeset <patch|minor|major> "<summary>"`. Required for any consumer-visible change. Skip only for changes that don't ship in `dist/` (e.g. `eslint.config.mjs`, internal tests, source-only comments). Keep it to 1-2 plain sentences: the consumer-visible change plus any migration note - changesets become the public CHANGELOG, so no rationale, investigation history, or noise (that belongs in the commit message and PR).
3. Commit code + changeset together with a Conventional Commits message:
```
git add packages/sdk/... .changeset/<slug>.md
Expand Down
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,11 @@ Don't want to install or clone anything yet? [Try the live demo](https://app.wor
Use Workflow Builder inside your own React app. No clone, no Docker. Install the SDK and its peer dependencies from npm:

```bash
npm install @workflowbuilder/sdk \
react react-dom \
@xyflow/react \
@jsonforms/core @jsonforms/react \
i18next react-i18next i18next-browser-languagedetector \
immer zustand
npm install @workflowbuilder/sdk @xyflow/react zustand
```

Requires React 18 or 19.

Render the editor:

```tsx
Expand Down
6 changes: 6 additions & 0 deletions apps/ai-studio/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Base URL of the reference backend the demo talks to.
VITE_BACKEND_URL=http://127.0.0.1:3001
# Cloudflare Turnstile site key (public). Leave empty to run the demo without
# bot protection (local dev). When set, the Run button attaches a Turnstile
# token that the backend verifies before executing a workflow.
VITE_TURNSTILE_SITE_KEY=
8 changes: 6 additions & 2 deletions apps/ai-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
"test:watch": "vitest --passWithNoTests"
},
"dependencies": {
"@jsonforms/core": "^3.4.1",
"@jsonforms/react": "^3.4.1",
"@phosphor-icons/react": "^2.1.7",
"@synergycodes/overflow-ui": "1.0.0-beta.27",
"@workflow-builder/types": "workspace:*",
"@xyflow/react": "catalog:",
"clsx": "^2.1.1",
"html-to-image": "1.11.11",
"immer": "^10.1.1",
"mermaid": "^11.15.0",
"react": "^19.1.0",
"react-dom": "catalog:",
"react-i18next": "^15.4.1",
"react-markdown": "^10.1.0",
"recharts": "^3.9.0",
"remark-gfm": "^4.0.1",
"zustand": "^5.0.1"
},
"devDependencies": {
Expand Down
20 changes: 16 additions & 4 deletions apps/ai-studio/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
import { WorkflowBuilder } from '@workflowbuilder/sdk';

import './node-overrides.css';
import '@workflowbuilder/sdk/style.css';

import logoDark from '../assets/workflow-builder-logo-white.svg';
import logoLight from '../assets/workflow-builder-logo.svg';
import { AiStudioControls } from '../components/controls/ai-studio-controls';
import { DisclaimerModal } from '../components/disclaimer/disclaimer-modal';
import { ExecutionHighlighting } from '../components/execution/highlighting';
import { ExecutionLogPanel } from '../components/execution/log-panel';
import { ExecutionNodeDetail } from '../components/execution/node-detail';
import { aiStudioTemplates } from '../data/ai-studio-templates';
import { aiStudioNodeTypes } from '../data/node-types';
import { supportTriageFlow } from '../data/support-triage-flow';
import { plugin as aiStudioFeaturesPlugin } from '../plugin';
import { plugin as undoRedoPlugin } from '../plugins/undo-redo/plugin-exports';

const flagship = supportTriageFlow.value;

export function App() {
return (
<WorkflowBuilder.Root
name="ai-studio"
name={flagship.name}
logo={{ light: logoLight, dark: logoDark }}
logoHref="https://workflowbuilder.io"
layoutDirection={flagship.layoutDirection}
initialNodes={flagship.diagram.nodes}
initialEdges={flagship.diagram.edges}
nodeTypes={aiStudioNodeTypes}
diagramTemplates={aiStudioTemplates}
plugins={[aiStudioFeaturesPlugin]}
plugins={[aiStudioFeaturesPlugin, undoRedoPlugin]}
>
<WorkflowBuilder.DefaultLayout />
<AiStudioControls />
<ExecutionLogPanel />
<ExecutionNodeDetail />
<ExecutionHighlighting />
<DisclaimerModal />
</WorkflowBuilder.Root>
);
}
3 changes: 3 additions & 0 deletions apps/ai-studio/src/app/node-overrides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[class^='_json-form-container_'] textarea {
font-family: ui-monospace, monospace;
}
Loading
Loading