The Comanda server provides a RESTful API interface for managing providers, environment configuration, and file operations. The goal is to have CLI and server functionality at parity so that you can build your own UI for managing Comanda agentic workflows.
When authentication is enabled, all endpoints require a Bearer token in the Authorization header:
Authorization: Bearer your-tokenThe provider management API allows you to configure and manage different AI model providers (OpenAI, Anthropic, Google, etc.).
GET /providers
Authorization: Bearer <token>Lists all configured providers and their available models.
Response:
{
"success": true,
"providers": [
{
"name": "openai",
"models": ["gpt-4", "gpt-3.5-turbo"],
"enabled": true
},
{
"name": "anthropic",
"models": ["claude-2", "claude-instant"],
"enabled": true
}
]
}PUT /providers
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "openai",
"apiKey": "your-api-key",
"models": ["gpt-4", "gpt-3.5-turbo"],
"enabled": true
}Updates or adds a provider configuration. If the provider doesn't exist, it will be created.
Response:
{
"success": true,
"message": "Provider openai updated successfully"
}DELETE /providers/{provider_name}
Authorization: Bearer <token>Removes a provider configuration.
Response:
{
"success": true,
"message": "Provider openai removed successfully"
}The environment security API provides endpoints for encrypting and decrypting the environment configuration file.
POST /env/encrypt
Authorization: Bearer <token>
Content-Type: application/json
{
"password": "your-password"
}Encrypts the environment file with the provided password. The original file will be replaced with an encrypted version.
Response:
{
"success": true,
"message": "Environment file encrypted successfully"
}POST /env/decrypt
Authorization: Bearer <token>
Content-Type: application/json
{
"password": "your-password"
}Decrypts the environment file using the provided password. The encrypted file will be replaced with the decrypted version.
Response:
{
"success": true,
"message": "Environment file decrypted successfully"
}The file operations API provides endpoints for managing files with enhanced metadata.
GET /list
Authorization: Bearer <token>Returns a list of files with detailed metadata including creation date, modification date, and supported methods.
Response:
{
"success": true,
"files": [
{
"name": "example.yaml",
"path": "example.yaml",
"size": 1234,
"isDir": false,
"createdAt": "2024-03-21T10:00:00Z",
"modifiedAt": "2024-03-21T10:00:00Z",
"methods": "GET"
}
]
}The methods field indicates whether a YAML file accepts input:
GET: File can be processed without inputPOST: File requires input for processing
POST /files
Authorization: Bearer <token>
Content-Type: application/json
{
"path": "example.yaml",
"content": "your file content"
}Creates a new file with the specified content. The path must be relative to the data directory.
Response:
{
"success": true,
"message": "File created successfully",
"file": {
"name": "example.yaml",
"path": "example.yaml",
"size": 1234,
"isDir": false,
"createdAt": "2024-03-21T10:00:00Z",
"modifiedAt": "2024-03-21T10:00:00Z"
}
}PUT /files?path=example.yaml
Authorization: Bearer <token>
Content-Type: application/json
{
"content": "updated content"
}Updates an existing file with new content.
Response:
{
"success": true,
"message": "File updated successfully",
"file": {
"name": "example.yaml",
"path": "example.yaml",
"size": 1234,
"isDir": false,
"createdAt": "2024-03-21T10:00:00Z",
"modifiedAt": "2024-03-21T10:00:00Z"
}
}DELETE /files?path=example.yaml
Authorization: Bearer <token>Deletes the specified file.
Response:
{
"success": true,
"message": "File deleted successfully"
}POST /files/upload?runtimeDir=uploads
Authorization: Bearer <token>
Content-Type: multipart/form-data
Form fields:
- file: (binary file data)
- path: "path/to/file.ext"Uploads a file using multipart/form-data format. The file will be saved at the specified path. The optional runtimeDir query parameter specifies a subdirectory within the data directory for organizing uploads.
Response:
{
"success": true,
"message": "File uploaded successfully"
}GET /files/content?path=example.txt
Authorization: Bearer <token>
Accept: text/plainRetrieves the content of a file as plain text.
Response:
File content as plain text
GET /files/download?path=example.pdf
Authorization: Bearer <token>
Accept: application/octet-streamDownloads a file in binary format. The response will be the raw file content with appropriate content type.
Response: Binary file content
GET /health
Authorization: Bearer <token>Returns the current health status of the server.
Response:
{
"success": true,
"message": "Server is healthy",
"statusCode": 200,
"response": "OK"
}POST /yaml/upload?runtimeDir=myproject
Authorization: Bearer <token>
Content-Type: application/json
{
"content": "your yaml content here"
}Uploads a YAML file for processing. The optional runtimeDir query parameter specifies a subdirectory within the data directory for organizing YAML scripts.
Response:
{
"success": true,
"message": "YAML file uploaded successfully"
}POST /yaml/process?runtimeDir=myproject
Authorization: Bearer <token>
Content-Type: application/json
# Regular processing (JSON response)
{
"content": "your yaml content here",
"streaming": false
}
# Streaming processing (Server-Sent Events)
{
"content": "your yaml content here",
"streaming": true
}For streaming requests, also include:
Accept: text/event-streamRegular processing response:
{
"success": true,
"yaml": "processed yaml content"
}Streaming response (Server-Sent Events):
data: Processing step 1...
data: Model response: ...
data: Processing step 2...
data: Processing complete
The generate endpoint allows you to generate Comanda workflow YAML files using an LLM based on natural language prompts.
POST /generate
Authorization: Bearer <token>
Content-Type: application/json
{
"prompt": "Create a workflow to summarize a file and save it",
"model": "gpt-4o-mini" # Optional, uses default_generation_model if not specified
}Generates a new Comanda workflow YAML file based on the provided prompt.
Request parameters:
prompt(required): Natural language description of the workflow you want to createmodel(optional): Specific model to use for generation. If not provided, uses thedefault_generation_modelfrom configuration
Response:
{
"success": true,
"yaml": "step_one:\n model: gpt-4o-mini\n input: FILE\n ...",
"model": "gpt-4o-mini"
}Error response:
{
"success": false,
"error": "No model specified and no default_generation_model configured"
}The process endpoint handles YAML file processing via POST requests only, supporting both regular and streaming responses.
POST /process?filename=example.yaml&runtimeDir=myproject
Authorization: Bearer <token>
Content-Type: application/json
# Process a YAML file with input
POST /process?filename=example.yaml&runtimeDir=myproject
Authorization: Bearer <token>
Content-Type: application/json
{
"input": "your input here",
"streaming": false # Set to true for Server-Sent Events streaming
}
# For streaming responses, include:
Accept: text/event-stream
# Response formats:
# Regular JSON response (streaming: false):
{
"success": true,
"message": "Successfully processed example.yaml",
"output": "Response from gpt-4o-mini:\n..."
}
# Server-Sent Events response (streaming: true):
data: Processing step 1...
data: Model response: ...
data: Processing step 2...
data: Processing complete
# Error response (method not allowed):
{
"success": false,
"error": "YAML processing is only available via POST requests. Please use POST with your YAML content."
}Note: All YAML processing must be done via POST requests. The endpoint no longer supports GET requests for processing.
- Bearer token authentication when enabled
- Token must be provided in the Authorization header
- All endpoints check authentication if enabled
- Path traversal prevention
- Files are restricted to the data directory
- Proper permission checks on file operations
- Password-based encryption using AES-256-GCM
- Secure key derivation using SHA-256
- Base64 encoding for encrypted data
All endpoints return appropriate HTTP status codes and JSON responses with error messages:
{
"success": false,
"error": "Detailed error message"
}Common status codes:
- 200: Success
- 400: Bad Request (invalid input)
- 401: Unauthorized (missing or invalid token)
- 403: Forbidden (path traversal attempt)
- 404: Not Found (file or resource not found)
- 409: Conflict (file already exists)
- 500: Internal Server Error
- Process YAML with streaming:
# Process YAML content with streaming
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"content":"your yaml content", "streaming": true}' \
http://localhost:8080/yaml/process
# Process file with streaming
curl -H "Authorization: Bearer your-token" \
-H "Accept: text/event-stream" \
"http://localhost:8080/process?filename=example.yaml&streaming=true"
# Process file with input and streaming
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"input":"your input here", "streaming": true}' \
"http://localhost:8080/process?filename=example.yaml"- List Providers:
curl -H "Authorization: Bearer your-token" \
http://localhost:8080/providers- Update Provider:
curl -X PUT \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"name":"openai","apiKey":"your-api-key","models":["gpt-4"]}' \
http://localhost:8080/providers- Encrypt Environment:
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"password":"your-password"}' \
http://localhost:8080/env/encrypt- Create File:
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"path":"example.yaml","content":"your content"}' \
http://localhost:8080/files- Upload File:
curl -X POST \
-H "Authorization: Bearer your-token" \
-F "file=@/path/to/local/file.txt" \
-F "path=destination/file.txt" \
http://localhost:8080/files/upload- Get File Content:
curl -H "Authorization: Bearer your-token" \
-H "Accept: text/plain" \
http://localhost:8080/files/content?path=example.txt- Download File:
curl -H "Authorization: Bearer your-token" \
-H "Accept: application/octet-stream" \
http://localhost:8080/files/download?path=example.pdf \
--output downloaded_file.pdf- Generate Workflow:
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"prompt":"Create a workflow to read a CSV file and summarize its contents"}' \
http://localhost:8080/generate
# With specific model
curl -X POST \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"prompt":"Create a workflow to analyze sentiment from user input","model":"gpt-4o"}' \
http://localhost:8080/generate// Base configuration
const API_URL = 'http://localhost:8080';
const TOKEN = 'your-token';
const headers = {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json'
};
// Process YAML with streaming
async function processYAMLStreaming(content) {
const response = await fetch(`${API_URL}/yaml/process`, {
method: 'POST',
headers: {
...headers,
'Accept': 'text/event-stream'
},
body: JSON.stringify({ content, streaming: true })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = decoder.decode(value);
// Handle each SSE message
console.log(text);
}
}
// Process file with streaming
async function processFileStreaming(filename, input = null) {
const url = `${API_URL}/process?filename=${encodeURIComponent(filename)}${input ? '' : '&streaming=true'}`;
const options = {
method: input ? 'POST' : 'GET',
headers: {
...headers,
'Accept': 'text/event-stream'
}
};
if (input) {
options.body = JSON.stringify({ input, streaming: true });
}
const response = await fetch(url, options);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = decoder.decode(value);
// Handle each SSE message
console.log(text);
}
}
// List providers
async function listProviders() {
const response = await fetch(`${API_URL}/providers`, { headers });
return await response.json();
}
// Update provider
async function updateProvider(provider) {
const response = await fetch(`${API_URL}/providers`, {
method: 'PUT',
headers,
body: JSON.stringify(provider)
});
return await response.json();
}
// Encrypt environment
async function encryptEnvironment(password) {
const response = await fetch(`${API_URL}/env/encrypt`, {
method: 'POST',
headers,
body: JSON.stringify({ password })
});
return await response.json();
}
// Create file
async function createFile(path, content) {
const response = await fetch(`${API_URL}/files`, {
method: 'POST',
headers,
body: JSON.stringify({ path, content })
});
return await response.json();
}
// Update file
async function updateFile(path, content) {
const response = await fetch(`${API_URL}/files?path=${encodeURIComponent(path)}`, {
method: 'PUT',
headers,
body: JSON.stringify({ content })
});
return await response.json();
}
// Delete file
async function deleteFile(path) {
const response = await fetch(`${API_URL}/files?path=${encodeURIComponent(path)}`, {
method: 'DELETE',
headers
});
return await response.json();
}
// Upload file
async function uploadFile(file, path) {
const formData = new FormData();
formData.append('file', file);
formData.append('path', path);
const response = await fetch(`${API_URL}/files/upload`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${TOKEN}`
},
body: formData
});
return await response.json();
}
// Get file content
async function getFileContent(path) {
const response = await fetch(`${API_URL}/files/content?path=${encodeURIComponent(path)}`, {
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Accept': 'text/plain'
}
});
return await response.text();
}
// Download file
async function downloadFile(path) {
const response = await fetch(`${API_URL}/files/download?path=${encodeURIComponent(path)}`, {
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Accept': 'application/octet-stream'
}
});
return await response.blob();
}
// Generate workflow
async function generateWorkflow(prompt, model = null) {
const body = { prompt };
if (model) {
body.model = model;
}
const response = await fetch(`${API_URL}/generate`, {
method: 'POST',
headers,
body: JSON.stringify(body)
});
return await response.json();
}
// Example usage
async function example() {
try {
// Process YAML with streaming
await processYAMLStreaming(`
step_one:
model: gpt-4o
input: "Hello"
output: STDOUT
`);
// Process file with streaming
await processFileStreaming('example.yaml', 'optional input here');
// List providers
const providers = await listProviders();
console.log('Providers:', providers);
// Update OpenAI provider
const updateResult = await updateProvider({
name: 'openai',
apiKey: 'your-api-key',
models: ['gpt-4', 'gpt-3.5-turbo'],
enabled: true
});
console.log('Update result:', updateResult);
// Create a file
const createResult = await createFile('example.yaml', 'file content');
console.log('Create result:', createResult);
// Generate a workflow
const generateResult = await generateWorkflow(
'Create a workflow to read a CSV file and summarize its contents'
);
console.log('Generated YAML:', generateResult.yaml);
// Generate with specific model
const generateWithModel = await generateWorkflow(
'Create a workflow to analyze sentiment from user input',
'gpt-4o'
);
console.log('Generated with model:', generateWithModel.model);
} catch (error) {
console.error('Error:', error);
}
}-
Always handle errors appropriately:
- Check response status codes
- Parse error messages from responses
- Implement proper error handling in your code
-
Secure your bearer token:
- Never expose it in client-side code
- Use environment variables or secure configuration
- Rotate tokens periodically
-
File operations:
- Always use relative paths
- Validate file content before sending
- Handle large files appropriately
-
Environment security:
- Use strong passwords for encryption
- Store passwords securely
- Keep backup of environment file before encryption
-
Provider management:
- Validate API keys before saving
- Keep track of enabled/disabled providers
- Monitor model availability
-
Streaming:
- Use streaming for long-running operations
- Handle SSE events appropriately
- Implement proper error handling for stream disconnections
- Consider fallback to non-streaming for older browsers