Note
This repo is no longer being actively developed and is here for historical purposes. Its features and functionality have largely been superceded by the DAZ Script Server project, located here: https://github.com/bluemoonfoundry/daz-script-server
A powerful, configuration-driven command-line utility system that provides standardized automation scripts for DAZ Studio. This toolkit acts as a bridge between Python and DAZ Studio's scripting language (DSA), offering multiple interaction modes including CLI, interactive shell, REST API server, GUI, and a modern web interface (Pro mode).
- Features
- Architecture
- Prerequisites
- Installation
- Configuration
- Usage
- DAZ Script Server
- Available Commands
- Adding New Commands
- Development
- License
- Configuration-Driven: All commands defined in a central YAML configuration file
- Multiple Interface Modes: CLI, interactive shell, REST API, GUI, and Pro web interface
- Dynamic Command Loading: Commands are loaded and parsed at runtime
- Extensible Architecture: Easy to add new commands without modifying core code
- Type-Safe Arguments: Automatic argument validation and type conversion
- Auto-Generated Documentation: Command reference documentation generated from config
- DAZ Studio Integration: Seamless execution of DAZ Studio scripts via subprocess or a high-performance Script Server plugin
- Smart Autocomplete: Scene-aware autocomplete in Interactive and Pro modes (requires Script Server)
- Pro Mode: Modern, dark-themed web interface with dynamic forms and real-time feedback
┌─────────────────────────────────────────────────────────────┐
│ User Input │
│ (CLI Args / Interactive Shell / HTTP Request / GUI / Pro) │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ vangard/main.py (Entry Point) │
│ Routes to: cli.py, interactive.py, server.py, │
│ gui.py, or pro.py │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ core/framework.py (Core Engine) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ apply_startup_flags() - Processes CLI startup flags │ │
│ │ load_config() - Loads config.yaml │ │
│ │ build_parser() - Creates argparse from config │ │
│ │ load_class() - Dynamically imports command class│ │
│ │ run_command() - Executes command instance │ │
│ └─────────────────────────────────────────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ vangard/commands/ (Python Command Classes) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ BaseCommand (Abstract Base) │ │
│ │ - process(args) │ │
│ │ - exec_remote_script(script_name, vars) │ │
│ │ - to_dict(args) │ │
│ └──────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┴──────────────────────────────┐ │
│ │ Specific Commands (inherit from BaseCommand) │ │
│ │ LoadMergeSU, BatchRenderSU, CreateGroupNodeSU, etc. │ │
│ └─────────────────────────────────────────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Execution Mode (selected by DAZ_SCRIPT_SERVER_ENABLED env var) │
│ │
│ Subprocess (default) │ DAZ Script Server │
│ DAZ Studio launched with │ POST /execute to running server │
│ -scriptArg (JSON) + .dsa path │ {"scriptFile": "...", "args": {}} │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ vangard/scripts/ (DAZ Studio Script Files) │
│ LoadMergeSU.dsa, BatchRenderSU.dsa, ... (one per command) │
└─────────────────────────────────────────────────────────────┘
- config.yaml: Central configuration defining all commands, arguments, and class mappings
- core/framework.py: Core engine that loads config, builds parsers, applies startup flags, and executes commands
- vangard/commands/: Python command classes that process arguments and launch DAZ scripts
- vangard/scripts/: DAZ Studio script files (.dsa) that perform actual operations in DAZ
- vangard/scene_cache.py: Polls DAZ Studio via the Script Server for scene node data, used for smart autocomplete
- Interface Layers: CLI, Interactive Shell, FastAPI Server, GUI, and Pro web interface
- User invokes command through any interface mode
- Interface layer parses input and passes to core framework
- Framework loads corresponding command class from config.yaml
- Command class processes arguments and converts to JSON
- Command executes via DAZ Studio subprocess (default) or sends REST request to Script Server plugin
- DAZ Studio script executes operation and returns results
- Python: 3.8 or higher
- DAZ Studio: Installed and configured
- Operating System: Windows, macOS, or Linux
-
Clone the repository:
git clone https://github.com/bluemoonfoundry/vangard-script-utils.git cd vangard-script-utils -
Install the package (editable mode for development):
pip install -e .This installs the package and creates console script commands:
vangard- Multi-mode launchervangard-cli- Direct CLI accessvangard-interactive- Interactive shellvangard-server- FastAPI servervangard-gui- GUI interfacevangard-pro- Pro web interface
-
Set up environment variables: Create a
.envfile in the root directory:# DAZ Studio subprocess mode (default) DAZ_ROOT=/path/to/daz/studio/executable DAZ_ARGS=--optional-daz-arguments # DAZ Script Server plugin mode (optional) DAZ_SCRIPT_SERVER_ENABLED=false DAZ_SCRIPT_SERVER_HOST=127.0.0.1 DAZ_SCRIPT_SERVER_PORT=18811
Subprocess mode (default):
DAZ_ROOT: Absolute path to your DAZ Studio executable- Windows:
C:/Program Files/DAZ 3D/DAZStudio4/DAZStudio.exe - macOS:
/Applications/DAZ 3D/DAZStudio4 64-bit/DAZStudio.app/Contents/MacOS/DAZStudio
- Windows:
DAZ_ARGS: Optional additional arguments to pass to DAZ Studio
DAZ Script Server mode (optional — see DAZ Script Server):
DAZ_SCRIPT_SERVER_ENABLED: Set totrueto enable server mode instead of subprocess. Default:falseDAZ_SCRIPT_SERVER_HOST: Hostname or IP of the DAZ Script Server. Default:127.0.0.1DAZ_SCRIPT_SERVER_PORT: Port of the DAZ Script Server. Default:18811
pip install -r requirements.txt
python -m vangard.main [mode] [arguments]For detailed installation instructions, see INSTALLATION.md.
All commands are defined in config.yaml. The configuration structure:
app:
prog: "vangard-cli"
description: "A command-line interface for the Vangard toolkit."
commands:
- name: "command-name"
class: "vangard.commands.CommandClassName.CommandClassName"
help: "Description of what the command does"
arguments:
- names: ["positional_arg"]
dest: "positional_arg"
type: "str"
required: true
help: "Description of positional argument"
- names: ["-o", "--option"]
dest: "option"
type: "str"
default: null
help: "Description of optional argument"type value |
Python type |
|---|---|
str |
str |
int |
int |
float |
float |
(omit type, use action: "store_true") |
bool flag |
For list arguments, add nargs: "*" or nargs: "+".
Each argument can include a ui block that controls how it is rendered in Pro mode and the GUI. This is ignored by the CLI.
arguments:
- names: ["scene_file"]
dest: "scene_file"
type: "str"
ui:
widget: "file-picker"
file_type: "file"
extensions: [".duf", ".dsf"]
mode: "save" # "save" or "open" (for file-picker)Available widgets:
widget |
Description | Extra keys |
|---|---|---|
text |
Plain text input | placeholder |
number |
Numeric input | min, max, step |
spinner |
Integer stepper | min, max, step |
slider |
Range slider | min, max, step, show_value |
checkbox |
Boolean toggle | — |
radio |
Radio button group | choices |
select |
Dropdown list | choices |
textarea |
Multi-line text | rows, placeholder |
file-picker |
File path input with browse button | file_type, extensions, mode |
folder-picker |
Folder path input with browse button | — |
choices format (for select and radio):
choices:
- value: "direct-file"
label: "Direct to File (Local)"
- value: "local-to-window"
label: "Local to Window"
# or simple string list:
choices: ["png", "jpg", "tif"]Arguments that accept scene node names can be configured for smart autocomplete. This requires the DAZ Script Server to be active (see DAZ Script Server).
arguments:
- names: ["target_node"]
dest: "target_node"
type: "str"
autocomplete:
source: "scene-nodes"
types: ["figure", "prop", "camera"] # optional — omit to include all typesNode types available for filtering: camera, light, figure, prop, group, conformer
When types is omitted, all non-bone scene nodes are offered as suggestions.
After installation, you can use the package in two ways:
vangard-cli [command] [arguments]
# or
vangard cli [command] [arguments]python -m vangard.cli [command] [arguments]
# or
python -m vangard.main cli [command] [arguments]All modes support the following flag:
| Flag | Description |
|---|---|
--enable-script-server |
Enable DAZ Script Server mode for this session, overriding the DAZ_SCRIPT_SERVER_ENABLED environment variable. Requires DAZ Studio to be running with the Script Server plugin active — the tool will exit with a diagnostic message if the server cannot be reached. |
Examples:
vangard-interactive --enable-script-server
vangard-pro --enable-script-server
vangard-server --enable-script-server
vangard interactive --enable-script-serverExecute single commands from the command line:
vangard-cli [command] [arguments]Examples:
# Load a scene file
vangard-cli load-scene /path/to/scene.duf
# Load and merge a scene
vangard-cli load-scene /path/to/scene.duf --merge
# Render current scene
vangard-cli scene-render -o /path/to/output.png
# Batch render multiple scenes
vangard-cli batch-render -s "/path/to/scenes/*.duf" -o /output/dir
# Create a camera
vangard-cli create-cam "MyCamera" "PerspectiveCamera" --focus
# Save scene with incremented filename
vangard-cli inc-scene
# Capture the active viewport to image files
vangard-cli save-viewport -f C:/output/frame
# Get help for a specific command
vangard-cli help batch-renderLaunch an interactive shell with command history and auto-completion:
vangard-interactive
vangard-interactive --enable-script-serverOnce in the shell:
vangard-cli> load-scene /path/to/scene.duf
vangard-cli> create-cam "Camera1" "PerspectiveCamera"
vangard-cli> scene-render -o /path/to/output.png
vangard-cli> exit
Interactive Mode Features:
- Command auto-completion (press Tab)
- Command history (use Up/Down arrows)
- Persistent history across sessions (stored in
.cli_history) - Scene-aware autocomplete: When started with
--enable-script-server, argument fields marked withautocomplete: scene-nodesinconfig.yamlsuggest live node names from the current DAZ Studio scene
Special Commands (available when Script Server is enabled):
| Command | Shortcut | Description |
|---|---|---|
.refresh |
.r |
Immediately refresh the scene node cache |
.stats |
.s |
Show scene cache statistics (node counts, staleness, polling status) |
.help |
.h, .? |
Show available special commands |
Run as a FastAPI REST API server:
vangard-server
vangard-server --enable-script-serverThe server starts at http://127.0.0.1:8000 with:
- Interactive API documentation: http://127.0.0.1:8000/docs
- OpenAPI schema: http://127.0.0.1:8000/openapi.json
API Endpoints:
GET /: Health checkPOST /api/{command-name}: Execute command (one endpoint per command in config.yaml)
Example API Request:
curl -X POST "http://127.0.0.1:8000/api/load-scene" \
-H "Content-Type: application/json" \
-d '{"scene_file": "/path/to/scene.duf", "merge": false}'Launch a simple graphical interface:
vangard-gui
vangard-gui --enable-script-serverLaunch the professional web interface — a modern, dark-themed browser UI with dynamic forms, real-time feedback, and command discovery:
vangard-pro
vangard-pro --enable-script-serverThe server starts at http://127.0.0.1:8000/ui.
Pro Mode Features:
- Visual Command Browser: Searchable sidebar listing all available commands with icons and descriptions
- Dynamic Forms: Forms are auto-generated from
config.yaml— respecting widget types, choices, file pickers, sliders, etc. - Scene Autocomplete: When started with
--enable-script-server, text fields withautocomplete: scene-nodesare populated with live node names from the current DAZ Studio scene - Real-time Output: Color-coded results (green = success, red = error) with timestamps
- Dark/Light Theme: Toggle via the toolbar
- API Access: Swagger UI still available at
/docs
Pro Mode Scene API (additional endpoints available when running Pro mode):
| Endpoint | Method | Description |
|---|---|---|
/api/scene/nodes |
GET | Get cached scene nodes, optionally filtered by node_type and name_filter |
/api/scene/labels |
GET | Get node label strings for autocomplete, optionally filtered by node_type |
/api/scene/refresh |
POST | Force an immediate scene cache refresh |
/api/scene/stats |
GET | Get cache statistics (node counts, last update time, polling status) |
See PRO_MODE.md for full documentation on Pro mode customization.
Mode Comparison:
| Feature | CLI | Interactive | GUI | Server | Pro |
|---|---|---|---|---|---|
| Visual Interface | ❌ | ❌ | ✅ | ❌ | ✅ |
| No Command Syntax Required | ❌ | ❌ | ✅ | ✅ | ✅ |
| Form-based Input | ❌ | ❌ | ✅ | ✅ | ✅ |
| Modern Design | ❌ | ❌ | ❌ | N/A | ✅ |
| Real-time Feedback | ✅ | ✅ | ✅ | ✅ | ✅ |
| Scene Autocomplete | ❌ | ✅* | ❌ | ❌ | ✅* |
| API Access | ❌ | ❌ | ❌ | ✅ | ✅ |
| Web-based | ❌ | ❌ | ❌ | ✅ | ✅ |
*Requires --enable-script-server or DAZ_SCRIPT_SERVER_ENABLED=true
The DAZ Script Server is an optional DAZ Studio plugin that enables a high-performance REST API interface for script execution. It is available as a separate repository: vangard-daz-script-server.
- No subprocess launch overhead: Scripts execute inside an already-running DAZ Studio instance
- Scene awareness: The script server enables the scene cache, which powers smart autocomplete in Interactive and Pro modes
- Faster iteration: Commands return results immediately rather than waiting for DAZ Studio to start
Option 1 — Startup flag (per session, no .env change needed):
vangard-pro --enable-script-serverIf the server cannot be reached, the tool exits immediately with a diagnostic message listing what to check.
Option 2 — Environment variable (persistent via .env):
DAZ_SCRIPT_SERVER_ENABLED=true
DAZ_SCRIPT_SERVER_HOST=127.0.0.1
DAZ_SCRIPT_SERVER_PORT=18811- DAZ Studio must be running
- The Script Server plugin must be installed and started inside DAZ Studio
- The server must be listening on the configured host and port
When server mode is active, commands are sent as a POST request to http://<host>:<port>/execute:
{
"scriptFile": "/absolute/path/to/Script.dsa",
"args": { "arg_name": "value" }
}The server responds with:
{
"success": true,
"result": "<last evaluated expression>",
"output": ["lines printed by the script"],
"error": null
}When the Script Server is enabled, the scene cache (vangard/scene_cache.py) polls DAZ Studio every 30 seconds to retrieve the current scene node hierarchy. This data powers:
- Tab-completion of node names in Interactive mode
- Autocomplete datalists in Pro mode form fields
Nodes returned by the cache are classified into: camera, light, figure (root characters only), prop, group, and conformer (clothing/hair attached to a figure). Bones are excluded from the cache as they add noise without being useful for autocomplete.
For a complete reference of all commands and their arguments, see config_reference.md.
Common Commands:
load-scene: Load or merge DAZ Studio scene filesbatch-render: Batch render multiple scenes with customizable optionsscene-render: Render the current scenecreate-cam: Create a new camera in the scenecreate-group: Group selected nodescopy-camera: Copy camera settings between camerastransform-copy: Copy transforms (translate/rotate/scale) between nodesapply-pose: Apply pose files to charactersrotate-render: Rotate object and render at intervalsinc-scene: Save scene with incremented filenameproduct-list: List products used in current scenesave-subset: Save selected items to scene subset filesave-viewport: Capture the active viewport to image files for a range of framesface-render-lora: Render orbital camera angles around a figure's face for LoRA training image generation
commands:
- name: "my-new-command"
class: "vangard.commands.MyNewCommandSU.MyNewCommandSU"
help: "Description of what your command does"
arguments:
- names: ["required_arg"]
dest: "required_arg"
type: "str"
required: true
help: "Description of required argument"
ui:
widget: "text"
placeholder: "Enter value"
- names: ["-o", "--optional-arg"]
dest: "optional_arg"
type: "int"
default: 0
help: "Description of optional argument"
ui:
widget: "spinner"
min: 0
max: 100
step: 1Create vangard/commands/MyNewCommandSU.py:
from vangard.commands.BaseCommand import BaseCommand
class MyNewCommandSU(BaseCommand):
# Default implementation calls MyNewCommandSU.dsa script.
# Override process() only if you need custom Python-side logic.
passCreate vangard/scripts/MyNewCommandSU.dsa:
includeDir_oFILE = DzFile( getScriptFileName() );
util_path = includeDir_oFILE.path() + "/DazCopilotUtils.dsa";
include(util_path);
function MyNewCommandSU() {
sFunctionName = 'MyNewCommandSU';
oScriptVars = init_script_utils(sFunctionName);
var sRequiredArg = oScriptVars['required_arg'];
var nOptionalArg = oScriptVars['optional_arg'] || 0;
// Implement your DAZ Studio logic here
log_success_event('Command executed successfully');
close_script_utils();
}
MyNewCommandSU();Available utility libraries to include alongside DazCopilotUtils.dsa:
DazCameraUtils.dsa, DazCoreUtils.dsa, DazFileUtils.dsa, DazLoggingUtils.dsa,
DazNodeUtils.dsa, DazRenderUtils.dsa, DazStringUtils.dsa, DazTransformUtils.dsa
python generate_docs.pyCreate tests/commands/test_my_new_command_su.py following the pattern of existing test files:
pytest tests/commands/test_my_new_command_su.py -v
pytest tests/integration/ # Verify config consistencyvangard-script-utils/
├── config.yaml # Command definitions (central registry)
├── config_reference.md # Auto-generated command reference
├── generate_docs.py # Regenerates config_reference.md
├── pyproject.toml # Package configuration
├── requirements.txt # Python dependencies
├── core/
│ └── framework.py # Core engine (config loading, arg parsing, startup flags)
├── vangard/
│ ├── main.py # Multi-mode entry point
│ ├── cli.py # CLI interface
│ ├── interactive.py # Interactive shell
│ ├── server.py # FastAPI server
│ ├── gui.py # GUI interface
│ ├── pro.py # Pro web interface
│ ├── scene_cache.py # Scene node cache for autocomplete
│ ├── interactive_completer.py # Smart tab-completion for interactive mode
│ ├── static/ # Pro mode frontend assets
│ │ ├── index.html
│ │ ├── css/styles.css
│ │ └── js/app.js
│ ├── commands/ # Python command classes
│ │ ├── BaseCommand.py # Abstract base class
│ │ └── ...
│ └── scripts/ # DAZ Studio scripts
│ ├── DazCopilotUtils.dsa # Utility facade (includes all below)
│ ├── DazCoreUtils.dsa
│ ├── DazLoggingUtils.dsa
│ ├── DazFileUtils.dsa
│ ├── DazStringUtils.dsa
│ ├── DazNodeUtils.dsa
│ ├── DazTransformUtils.dsa
│ ├── DazCameraUtils.dsa
│ ├── DazRenderUtils.dsa
│ ├── GetSceneHierarchySU.dsa # Scene cache query script
│ └── ... # One .dsa per command
└── tests/
├── conftest.py # Shared fixtures
├── commands/ # Command tests
├── unit/ # Unit tests
└── integration/ # Integration tests
PyYAML>=6.0: YAML configuration parsingfastapi>=0.85.0: REST API server frameworkuvicorn[standard]>=0.18.3: ASGI server for FastAPIprompt-toolkit>=3.0.0: Interactive shell featurespython-dotenv: Environment variable management
# Run all tests
pytest tests/
# Run specific test categories
pytest tests/commands/ # Command tests
pytest tests/unit/ # Unit tests
pytest tests/integration/ # Integration tests
# Run with coverage
pytest tests/ --cov=vangard --cov=core --cov-report=html
# Run a single test
pytest tests/unit/test_framework.py::test_load_config -vTest Markers:
unit— Fast tests with no external dependenciesintegration— Integration tests (no DAZ Studio required)command— Individual command testse2e— End-to-end tests (require DAZ Studio, excluded by default)manual— Manual tests (excluded by default)
- Command Class Naming:
CommandNameSU(suffix "SU" = Script Utility) - Script File Naming: Must match class name:
CommandNameSU.dsa - CLI Command Names: Use kebab-case (e.g.,
load-scene,batch-render) - Argument Naming: Use underscores in Python/config (e.g.,
scene_file)
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
Copyright (C) 2025 Blue Moon Foundry Software
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See LICENSE for the full license text.
Maintained by: Blue Moon Foundry Software Repository: https://github.com/bluemoonfoundry/vangard-script-utils