Usage
Workforce provides several ways to create and run workflows. You can use the GUI for visual editing, or the CLI for programmatic control.
Command Line Interface
Launching the GUI
To launch the Workforce GUI:
wf
or:
python -m workforce
To open a specific workflow file:
wf path/to/workflow.graphml
If a Workfile exists in the current directory, it will be opened automatically:
wf
Running Workflows
Execute a complete workflow:
wf run Workfile
Execute specific nodes only:
wf run Workfile --nodes node1,node2,node3
Use command wrappers (prefix/suffix):
wf run Workfile --wrapper "docker run -it ubuntu"
Server Management
Start a server for a workflow:
wf server start Workfile
Stop a server:
wf server stop Workfile
List all running servers:
wf server list
Editing Workflows via CLI
Add a node to a workflow:
wf edit add-node Workfile "node_name" "command to run"
Remove a node:
wf edit remove-node Workfile "node_name"
Add an edge (dependency):
wf edit add-edge Workfile "source_node" "target_node"
Remove an edge:
wf edit remove-edge Workfile "source_node" "target_node"
Edit node command:
wf edit edit-node Workfile "node_name" "new command"
Edit node position (for GUI layout):
wf edit edit-node-position Workfile "node_name" x y
GUI Usage
The Workforce GUI provides an interactive visual editor for workflows.
Keyboard Shortcuts
Q - Save and exit
R - Run the workflow (or run selected nodes)
D / Delete / Backspace - Delete selected node(s)
E - Connect selected nodes in sequence (creates edges)
Shift+E - Clear edges from selected nodes
C - Clear status of selected nodes
Shift+C - Clear all statuses in workflow
S - View logs for selected node
P - Edit command wrapper
O - Load/open Workfile
Ctrl+S - Save workflow
Ctrl+Up / Ctrl+Down - Zoom in/out
Double-Click Canvas - Add new node at cursor position
Double-Click Node - Edit node command
Right-Click + Drag - Create edge from source to target node
Middle-Click Node - Select node
Left-Click + Drag Node - Move node(s)
Shift + Left-Click + Drag Canvas - Rectangle selection
Edge Types
Workforce supports two types of edges that define how nodes depend on each other. See the Glossary for detailed definitions.
Blocking Edges (Blocking Edge)
Blocking edges are the default edge type and enforce strict dependencies. When a node has blocking edges as inputs, it only transitions to run state after all incoming blocking edges are ready. This enforces sequential execution.
To create a blocking edge (default behavior):
Right-click on a source node
Drag to the target node
Release to create the edge
The edge will appear as a solid line
Via CLI:
wf edit add-edge Workfile "source_node" "target_node"
This creates a blocking edge by default.
Via REST API (blocking edge):
curl -X POST http://localhost:5049/workspace/workspace_id/add-edge \
-H "Content-Type: application/json" \
-d '{"source_id": "node-uuid-1", "target_id": "node-uuid-2", "edge_type": "blocking"}'
Via Python client:
from workforce.gui.client import ServerClient
client = ServerClient(server_url)
client.add_edge(source_id="node-uuid-1", target_id="node-uuid-2", edge_type="blocking")
Non-Blocking Edges (Non-Blocking Edge)
Non-blocking edges are soft triggers that allow immediate execution without waiting for other dependencies. When a non-blocking edge becomes ready, the target node immediately transitions to run state, allowing for flexible triggering and re-execution patterns.
To create a non-blocking edge:
Hold Ctrl+Shift
Right-click and drag from source node to target node
Release to create the non-blocking edge
The edge will appear as a dashed line
Via CLI:
wf edit add-edge Workfile "source_node" "target_node" --edge_type non-blocking
Via REST API (non-blocking edge):
curl -X POST http://localhost:5049/workspace/workspace_id/add-edge \
-H "Content-Type: application/json" \
-d '{"source_id": "node-uuid-1", "target_id": "node-uuid-2", "edge_type": "non-blocking"}'
Via Python client:
from workforce.gui.client import ServerClient
client = ServerClient(server_url)
client.add_edge(source_id="node-uuid-1", target_id="node-uuid-2", edge_type="non-blocking")
Updating Edge Types
Change an existing edge type:
Via CLI:
wf edit edit-edge-type Workfile "source_node" "target_node" "non-blocking"
Via REST API:
curl -X POST http://localhost:5049/workspace/workspace_id/edit-edge-type \
-H "Content-Type: application/json" \
-d '{"source_id": "node-uuid-1", "target_id": "node-uuid-2", "edge_type": "non-blocking"}'
Use Cases
Blocking Edges are appropriate for:
Sequential pipelines where each step must complete before the next starts
Workflows that conform to directed acyclic graph (DAG) structure
Ensuring all prerequisites are satisfied before proceeding
Traditional data processing pipelines (extract → transform → load)
Non-Blocking Edges are appropriate for:
Fan-out patterns where a single node triggers multiple independent branches
Workflows requiring node re-execution (e.g., error recovery, data reprocessing)
Event-driven execution where triggers are more important than strict ordering
Flexible workflows that don’t conform to strict DAG structure
Monitoring or signal nodes that notify multiple consumers
Mixed Edge Workflows
Workflows can combine both edge types for sophisticated execution patterns. For example:
A node with both blocking edges (ensuring prerequisites) and non-blocking edges (allowing immediate re-execution on external triggers)
Multiple independent branches triggered by a single source (blocking to first node, then non-blocking to branch starts)
See Dependency Resolution in the architecture documentation for detailed execution semantics
Creating Workflows
Launch the GUI with
wfDouble-click on the canvas to add a new node
Enter the bash command in the popup dialog
To create dependencies:
Right-click and drag from source node to target node, OR
Select multiple nodes and press ‘E’ to connect them in sequence
Save the workflow (press Ctrl+S or use File menu)
Running Workflows
From the GUI:
Click the ‘Run’ button or press ‘R’
If nodes are selected, only those nodes (and their dependencies) will run
Otherwise, the entire workflow executes
Node colors indicate status:
Light gray - Not started (empty status)
Light cyan - Ready to run (status:
run)Light blue - Currently running (status:
running)Light green - Completed successfully (status:
ran)Light coral/red - Failed (status:
fail)
Viewing Logs
To view output from a node:
Select the node (left-click)
Press ‘S’ to open the log viewer
In the log popup, press ‘S’ or Escape to close it
View combined stdout/stderr from the command execution
Workflow File Format
Workforce uses GraphML format to store workflows. Each node represents a bash command, and edges represent dependencies.
Example GraphML Structure
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns">
<key id="label" for="node" attr.name="label" attr.type="string"/>
<key id="status" for="node" attr.name="status" attr.type="string"/>
<key id="log" for="node" attr.name="log" attr.type="string"/>
<key id="x" for="node" attr.name="x" attr.type="string"/>
<key id="y" for="node" attr.name="y" attr.type="string"/>
<key id="wrapper" for="graph" attr.name="wrapper" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="abc-123-uuid">
<data key="label">wget https://example.com/data.csv</data>
<data key="status"></data>
<data key="x">100</data>
<data key="y">100</data>
</node>
<node id="def-456-uuid">
<data key="label">python process.py data.csv</data>
<data key="status"></data>
<data key="x">300</data>
<data key="y">100</data>
</node>
<edge id="edge-789-uuid" source="abc-123-uuid" target="def-456-uuid">
<data key="status"></data>
</edge>
</graph>
</graphml>
Node Attributes
id - Unique identifier (UUID)
label - The bash command to execute
status - Current execution status (“”, run, running, ran, fail)
log - Combined stdout/stderr from command execution
x, y - Node position in GUI canvas (stored as strings)
Edge Attributes
id - Unique identifier (UUID)
status - Edge status used for dependency tracking (“”, to_run)
Command Wrappers
Workforce allows you to wrap commands with a template that includes a {} placeholder. This is useful for running commands in different environments.
The wrapper is a command template where {} is replaced with the actual node command.
Wrapper Examples
tmux Integration
Send commands to tmux sessions:
wf run Workfile --wrapper 'tmux send-keys -t mysession "{}" C-m'
Remote Execution via SSH
Execute commands on a remote server:
wf run Workfile --wrapper 'ssh user@remote-server "{}"'
Docker Containers
Run commands inside Docker containers:
wf run Workfile --wrapper 'docker run -it ubuntu bash -c "{}"'
Conda Environments
Activate a conda environment before running:
wf run Workfile --wrapper 'conda run -n myenv bash -c "{}"'
Slurm Job Submission
Submit each command as a Slurm job:
wf run Workfile --wrapper 'sbatch --wrap="{}"'
Export to Bash Script
Generate a bash script without executing:
wf run Workfile --wrapper 'echo "{}" >> commands.sh'
Adding Sleep/Delay
Add delay before each command:
wf run Workfile --wrapper 'bash -c "sleep 1; {}"'
Python API
You can also use Workforce programmatically:
import workforce
from workforce.edit.graph import load_graph, add_node_to_graph, save_graph
# Load an existing workflow
G = load_graph('workflow.graphml')
# Add a new node (returns dict with node_id)
result = add_node_to_graph('workflow.graphml', 'echo "Hello World"', x=100, y=200)
print(f"Created node: {result['node_id']}")
# Note: Most graph operations save automatically
# Individual functions like add_node_to_graph() handle save internally
For running workflows programmatically, connect to the server:
from workforce import utils
# Compute workspace ID from file path
workspace_id = utils.compute_workspace_id('workflow.graphml')
# Get workspace URL (auto-discovers or starts server)
workspace_url = utils.get_workspace_url(workspace_id)
# Runner client connects to workspace URL and waits for node_ready events
# This is typically done by the run command, not manually
Remote GUI over LAN
You can run the GUI from a different machine and connect to a remote server.
Start the server with LAN binding on the host machine:
wf server stop
wf server start --host 0.0.0.0
List access URLs and share the workspace URL:
wf server ls
This shows both Local and LAN URLs. Use the LAN URL format:
http://<server_ip>:<port>/workspace/<workspace_id>
From the remote machine, launch the GUI directly to that URL:
wf gui http://<server_ip>:<port>/workspace/<workspace_id>
Notes:
Do not use any 127.* addresses across machines; those are loopback only.
macOS: Allow the Python process through the firewall when prompted.
Termux/Android: Ensure the app has network permissions and the device is on the same subnet.
WSL (Windows Subsystem for Linux)
WSL2 uses NAT and assigns a private IP to the Linux VM. Services bound inside WSL are reachable from the Windows host, but not automatically from other LAN machines.
Options to access the Workforce server from other machines:
Run the server on Windows (outside WSL)
# In Windows Python environment
wf server start --host 0.0.0.0
Then use the Windows host LAN IP in the URL.
Port forward from Windows host to WSL
Find the current WSL IP (inside WSL):
ip addr | grep 'inet '
Add a Windows port proxy to forward port 5049 to WSL (run in an elevated PowerShell or cmd):
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=5049 connectaddress=<WSL_IP> connectport=5049
Open firewall for inbound TCP 5049:
netsh advfirewall firewall add rule name="Workforce 5049" dir=in action=allow protocol=TCP localport=5049
Verify:
netsh interface portproxy show all
Use the Windows host LAN IP in the GUI URL:
wf gui http://<windows_host_ip>:5049/workspace/<workspace_id>
Note: WSL IP can change after restart. Recreate the portproxy if needed.
Security
For LAN use, Workforce enables CORS for Socket.IO in development. If exposing beyond LAN, use a reverse proxy (e.g., Nginx) with TLS and restrict allowed origins.