Skip to content
Open
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
79 changes: 79 additions & 0 deletions .github/workflows/workflow-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Workflow Integration Test

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
workflow-integration:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build just-bash
run: pnpm build

- name: Install workflow-vite example dependencies
run: pnpm install --no-frozen-lockfile
working-directory: examples/workflow-vite

- name: Start vite server
run: |
pnpm dev > /tmp/vite.log 2>&1 &
echo $! > /tmp/vite.pid
# Wait for server to be ready
for i in {1..30}; do
if curl -s http://localhost:3000 > /dev/null 2>&1; then
echo "Server is ready"
break
fi
echo "Waiting for server... ($i/30)"
sleep 1
done
working-directory: examples/workflow-vite

- name: Test workflow API
run: |
response=$(curl -s -X POST http://localhost:3000/api/bash)
echo "Response: $response"

# Check if response contains expected data
if echo "$response" | grep -q '"message":"Bash workflow completed"'; then
echo "Message check passed"
else
echo "Message check failed"
exit 1
fi

if echo "$response" | grep -q '"log":"created\\nstep2: modified\\nstep3: modified\\nstep4: modified\\n"'; then
echo "Log check passed"
else
echo "Log check failed"
exit 1
fi

echo "All checks passed!"

- name: Stop vite server
if: always()
run: |
if [ -f /tmp/vite.pid ]; then
kill $(cat /tmp/vite.pid) 2>/dev/null || true
fi

- name: Show server logs on failure
if: failure()
run: cat /tmp/vite.log || true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ todo/
.pnpm-store
.docs-test-tmp/
src/commands/python3/worker.js
.workflow-data
.output/
36 changes: 36 additions & 0 deletions examples/workflow-vite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Workflow Vite Example

This example demonstrates using just-bash with [Workflow](https://useworkflow.dev) for durable workflow execution.

## What it demonstrates

- **Serializable Bash instances**: The `Bash` class implements Workflow's serde protocol, allowing instances to be serialized between workflow steps
- **State preservation**: Filesystem state persists across serialization boundaries
- **Serial step execution**: Each step receives, modifies, and returns the Bash instance

## Running the example

```bash
# Install dependencies
pnpm install

# Run in development mode
pnpm dev
```

## How it works

The workflow plugin transforms functions marked with `"use step"` and `"use workflow"` directives into durable workflow steps.

Between steps, the Workflow runtime serializes all step outputs (including the `Bash` instance) and deserializes them when needed. This enables:

- **Durability**: Workflow state survives process restarts
- **Resumability**: Workflows can resume from the last completed step
- **Isolation**: Each step runs independently with serialized inputs

## Limitations

1. **Only InMemoryFs**: OverlayFs cannot be serialized (it wraps a real filesystem)
2. **Callbacks not serialized**: `logger`, `trace`, `sleep`, `secureFetch` must be re-configured after deserialize
3. **Custom commands**: Must be re-registered after deserialize
4. **Shell variables per-exec**: In just-bash, each `exec()` call is isolated; shell variables don't persist across calls (filesystem does)
16 changes: 16 additions & 0 deletions examples/workflow-vite/api/bash.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineEventHandler } from "nitro/h3";
import { Run, start } from "workflow/api";
import { serialBashWorkflow } from "../workflows/bash-workflow";

export default defineEventHandler(async () => {
const { runId } = await start(serialBashWorkflow, []);

// Wait for completion using the returnValue getter
const run = new Run(runId);
const result = await run.returnValue;

return {
message: "Bash workflow completed",
result,
};
});
21 changes: 21 additions & 0 deletions examples/workflow-vite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "workflow-vite-example",
"version": "1.0.0",
"description": "Vite app demonstrating Bash serialization with Workflow",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"just-bash": "file:../..",
"nitro": "3.0.1-alpha.2",
"workflow": "https://workflow-docs-14qh5q5oq.vercel.sh/workflow.tgz"
},
"devDependencies": {
"typescript": "^5.9.3",
"vite": "^7.0.0"
}
}
Loading