A CLI for managing DynamicWeb 10 solutions — environments, authentication, queries, commands, files, add-ins, databases, and Swift releases.
| Tool | Best for |
|---|---|
| DW CLI (this repo) | Automation, CI/CD pipelines, scripting, headless environments |
| DW Desktop | Day-to-day developer workflows with a GUI |
This branch targets the 2.0 preview.
- Node.js
^20.19.0or>=22.12.0
Install the preview from npm:
npm install -g @dynamicweb/cli@preview
dw --helpInstall from npm:
npm install -g @dynamicweb/cli
dw --helpInstall from source:
npm install
npm install -g .The 2.0 preview is a substantial overhaul focused on automation and modern authentication.
- Automation-first command output:
env,login,files,folders,query,command,install,database, andswiftnow support--output jsonso scripts and pipelines can consume structured results instead of plain console logs. - OAuth client credentials support: the CLI can now authenticate with OAuth 2.0
client_credentials, which makes headless and CI/CD usage much easier. - Better environment handling: protocol, host, and auth details are stored more cleanly in
~/.dwc, while one-off runs can still override host and credentials directly. - Improved file workflows: file import, export, recursive sync, raw archive export, progress reporting, and source-type override flags make file operations more predictable.
- Clearer error reporting: every command exits with code
1on failure. In JSON mode the failure is in the envelope'serrorsarray; otherwise it is printed to stderr. Successful runs write nothing to stderr, so scripts that treat stderr output as failure work as expected.
View all available commands:
dw --help
dw <command> --helpSet up an environment and log in with a user:
dw env dev
dw login
dw # shows current environment, user, protocol, and hostRun a query:
dw query HealthCheckThe default login flow uses a DynamicWeb user account. The CLI logs in, creates an API key, and stores it in ~/.dwc.
dw login
dw login <saved-user>
dw env <environment>A user-authenticated config typically looks like this:
{
"env": {
"dev": {
"host": "localhost:6001",
"protocol": "https",
"users": {
"DemoUser": {
"apiKey": "<keyPrefix>.<key>"
}
},
"current": {
"user": "DemoUser",
"authType": "user"
}
}
},
"current": {
"env": "dev"
}
}For service accounts, automation, and headless usage, the CLI also supports OAuth 2.0 client_credentials.
Configure an environment for OAuth:
export DW_CLIENT_ID=my-client-id
export DW_CLIENT_SECRET=my-client-secret
dw login --oauthRun a one-off command with OAuth flags instead of saved config:
dw query HealthCheck \
--host your-solution.example.com \
--auth oauth \
--clientIdEnv DW_CLIENT_ID \
--clientSecretEnv DW_CLIENT_SECRET \
--output jsonAn OAuth-enabled environment in ~/.dwc looks like this:
{
"env": {
"dev": {
"host": "localhost:6001",
"protocol": "https",
"auth": {
"type": "oauth_client_credentials",
"clientIdEnv": "DW_CLIENT_ID",
"clientSecretEnv": "DW_CLIENT_SECRET"
},
"current": {
"authType": "oauth_client_credentials"
}
}
},
"current": {
"env": "dev"
}
}Most API-driven commands support these global options:
-v,--verbose: enable verbose logging--host: use a host directly instead of the saved environment--protocol: set the protocol used with--hostand default tohttps--apiKey: use an API key for environmentless execution--oauth: use OAuth client credentials authentication (shorthand for--auth oauth)--auth: override authentication mode withuseroroauth--clientId: pass an OAuth client ID directly--clientSecret: pass an OAuth client secret directly--clientIdEnv: read the OAuth client ID from an environment variable--clientSecretEnv: read the OAuth client secret from an environment variable--transfer: howfiles,foldersandinstallmove files —auto(default),filetransferorlegacy
On solutions that include the File Transfer API (/Admin/Api/FileTransfer), files, folders and install use it automatically: uploads go in checksum-verified chunks and are swapped in atomically, exports are streamed file by file, and transient failures are retried safely. Older solutions keep using the original endpoints. Commands, options and JSON output are the same either way; meta.transfer in the JSON output says which was used.
Force one or the other with --transfer filetransfer|legacy, the DW_TRANSFER environment variable, or a saved setting (dw config --env.dev.transfer legacy). A forced filetransfer fails instead of falling back when the solution does not provide the API. --raw exports (keep the zip) only exist in the legacy transfer.
With the File Transfer API, dw files can also sync and deploy:
dw files diff ./dist Templates/Designs/MyDesign # what differs, changes nothing
dw files sync ./dist Templates/Designs/MyDesign --delete # upload only changes, remove what dist lacks
dw files deploy ./dist Templates/Designs/MyDesign --keep-backup # swap in the new version in one step
dw files rollback Templates/Designs/MyDesign # put the previous version back
dw files stat Templates/Designs/MyDesign/app.js --checksum
dw files usage Templates/Designs--fail-on-remote-change (on sync, deploy and --import) stops before changing anything if files were edited on the server since the last sync. --changed-only skips unchanged files on --import/--export, and --verify checks exported files against their SHA-256 on the server. What the CLI knows about synced files is kept in ~/.dwc under transferState. See cliv2-documentation.md for details.
Commands that support --output json return a machine-readable envelope with ok, status, data, errors, and meta fields. status is 0 on success; on failure it is the HTTP status code returned by the DynamicWeb API, or 1 when the failure happened in the CLI. The process exit code is 1 on any failure. Interactive prompts are never shown in JSON mode.
Examples:
dw env --list --output json
dw login --oauth --output json
dw query FileByName --name DefaultMail.html --output jsonRepresentative output:
{
"ok": true,
"command": "env",
"operation": "list",
"status": 0,
"data": [
{
"environments": ["dev", "staging"]
}
],
"errors": [],
"meta": {}
}Create, select, or inspect saved environments.
dw env dev
dw env dev --host https://dev.example.com # create or update without prompting
dw env --list
dw env --users
dw env --list --output jsonExample JSON output:
{
"ok": true,
"command": "env",
"operation": "select",
"status": 0,
"data": [
{
"environment": "dev",
"current": "dev"
}
],
"errors": [],
"meta": {}
}Log in interactively, configure OAuth, or switch between saved users for the current environment.
dw login
dw login DemoUser
dw login --oauth
dw login --output jsonExample JSON output:
{
"ok": true,
"command": "login",
"operation": "oauth-login",
"status": 0,
"data": [
{
"environment": "dev",
"authType": "oauth_client_credentials",
"clientIdEnv": "DW_CLIENT_ID",
"clientSecretEnv": "DW_CLIENT_SECRET",
"expires": "2026-04-13T14:22:31Z"
}
],
"errors": [],
"meta": {}
}List, export, import, delete, copy, and move files and directories in the DynamicWeb file archive.
Useful flags:
-l,--list: list directories-f,--includeFiles: include files in listings-e,--export: export from the environment to disk-i,--import: import from disk to the environment-d,--delete: delete a file or directory--empty: used with--delete, empties a directory instead of removing it--copy <dest>: copy a file or directory to a destination path--move <dest>: move a file or directory to a destination path-r,--recursive: recurse through subdirectories--raw: keep downloaded archives zipped--dangerouslyIncludeLogsAndCache: include log and cache folders during export, which is risky and usually not recommended-af,--asFile: force the source path to be treated as a file-ad,--asDirectory: force the source path to be treated as a directory
Examples:
dw files templates ./templates -fre
dw files system -lr
dw files templates/Translations.xml ./templates -e
dw files templates/templates.v1 ./templates -e -ad
dw files ./Files templates -i -r --output json
dw files /Templates/OldDesign --delete
dw files /Templates/MyDesign --copy /Templates/MyDesign-backup
dw files /Templates/OldName --move /Templates/ArchiveCreate, rename, move, delete, copy, and export directories. Where dw files detects file vs. directory from the path extension, dw folders always treats the path as a directory — no ambiguity.
Useful flags:
-c,--create: create the directory at<folderPath>--rename <newName>: rename the directory to a new name (keeps it in the same parent)-m,--move <dest>: move the directory to the given destination path-d,--delete: delete the directory--empty: used with--delete, empties the directory instead of removing it--copy <dest>: copy the directory to the given destination path-e,--export: export the directory to disk-o,--outPath: local destination for--export(defaults to.)--raw: keep exported archives zipped instead of extracting
Examples:
dw folders /Files/NewFolder --create
dw folders /Files/OldName --rename NewName
dw folders /Files/MyFolder --move /Files/Archive
dw folders /Files/MyFolder --delete
dw folders /Files/MyFolder --delete --empty
dw folders /Files/MyFolder --copy /Files/MyFolder-backup
dw folders /Files/MyFolder --export --outPath ./localExample JSON output:
{
"ok": true,
"command": "files",
"operation": "import",
"status": 0,
"data": [
{
"type": "upload",
"destinationPath": "templates",
"files": [
"/workspace/Files/Templates/DefaultMail.html"
],
"response": {
"message": "Upload completed"
}
}
],
"errors": [],
"meta": {
"filesProcessed": 1,
"chunks": 1
}
}Run admin API queries, inspect available parameters, or prompt for them interactively.
dw query FileByName -l
dw query FileByName --name DefaultMail.html --directorypath /Templates/Forms/Mail
dw query FileByName --interactive
dw query FileByName --name DefaultMail.html --output jsonExample JSON output:
{
"ok": true,
"command": "query",
"operation": "run",
"status": 0,
"data": [
{
"name": "DefaultMail.html",
"path": "/Templates/Forms/Mail/DefaultMail.html"
}
],
"errors": [],
"meta": {
"query": "FileByName"
}
}Run admin API commands and pass a JSON payload either inline or by file path.
dw command PageCopy --json '{ "model": { "SourcePageId": 1189, "DestinationParentPageId": 1129 } }'
dw command PageMove --json ./PageMove.json
dw command PageDelete --json '{ "id": "1383" }' --output jsonExample JSON output:
{
"ok": true,
"command": "command",
"operation": "run",
"status": 0,
"data": [
{
"success": true,
"message": "Command executed"
}
],
"errors": [],
"meta": {
"commandName": "PageDelete"
}
}dw command --list is reserved for command metadata, but it is not fully implemented yet.
Upload and install a .dll or .nupkg add-in into the current environment.
dw install ./bin/Release/net10.0/CustomProject.dll
dw install ./bin/Release/net10.0/CustomProject.dll --queue --output jsonExample JSON output:
{
"ok": true,
"command": "install",
"operation": "queue",
"status": 0,
"data": [
{
"type": "upload",
"destinationPath": "System/AddIns/Local",
"files": [
"/workspace/bin/Release/net10.0/CustomProject.dll"
],
"response": {
"message": "Upload completed"
}
},
{
"type": "install",
"filePath": "/workspace/bin/Release/net10.0/CustomProject.dll",
"filename": "CustomProject.dll",
"queued": true,
"response": {
"success": true,
"message": "Addin installed"
}
}
],
"errors": [],
"meta": {
"filePath": "./bin/Release/net10.0/CustomProject.dll",
"filesProcessed": 1,
"chunks": 1
}
}Write values directly into ~/.dwc when you want to script config updates.
dw config --env.dev.host localhost:6001Export the current environment database to a .bacpac file.
dw database ./backups --export
dw database ./backups --export --output jsonDownload the latest Swift release, a specific tag, or the nightly build.
dw swift -l
dw swift . --tag v2.3.0 --force
dw swift . --nightly --force
dw swift -l --output jsonFor CI/CD, prefer OAuth client credentials and JSON output.
- Store
DW_CLIENT_IDandDW_CLIENT_SECRETin your pipeline secret store. - Use
--hosttogether with--auth oauthfor ephemeral runners. - Add
--output jsonwhen you want reliable parsing in scripts.
Example:
dw query HealthCheck \
--host your-solution.example.com \
--auth oauth \
--clientIdEnv DW_CLIENT_ID \
--clientSecretEnv DW_CLIENT_SECRET \
--output jsonFor longer-lived runners, configure a saved environment once without any prompts:
dw env production --host https://your-solution.example.com
dw login --oauth --output jsonThis repository now includes a reusable QA harness in qa/README.md. It runs the CLI against a real DynamicWeb solution using OAuth client credentials, keeps its own isolated HOME, and can exercise both:
- saved-environment developer flow
- ephemeral CI/CD-style flow with
--host --auth oauth
The harness currently excludes database and swift.
Git Bash can rewrite relative paths in a way that interferes with CLI file operations. If you see the path-conversion warning, disable it for the session before running file commands:
export MSYS_NO_PATHCONV=1
dw files -iro ./ ./TestFolder --host <host> --apiKey <apiKey>If you do not want to change that setting, prefer ./-prefixed paths or use PowerShell or CMD instead.