{"id":32784697,"url":"https://github.com/studiowebux/bnaas-ce","last_synced_at":"2026-04-29T16:02:38.263Z","repository":{"id":322129537,"uuid":"1088273779","full_name":"studiowebux/bnaas-ce","owner":"studiowebux","description":"A powerful, flexible graph execution engine for Deno that supports HTTP requests, conditions, string interpolation, data manipulation, and custom functions. Perfect for building complex automation workflows, API orchestration, and data processing pipelines.","archived":false,"fork":false,"pushed_at":"2025-11-02T18:02:03.000Z","size":105,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-02T19:19:45.467Z","etag":null,"topics":["claude-code","cli","container","deno","http","linux-service","orchestration","pipeline","tasks","web-app","workflow"],"latest_commit_sha":null,"homepage":"https://botnetasaservice.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/studiowebux.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-02T16:49:49.000Z","updated_at":"2025-11-02T18:01:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/studiowebux/bnaas-ce","commit_stats":null,"previous_names":["studiowebux/bnaas-ce"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/studiowebux/bnaas-ce","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/studiowebux%2Fbnaas-ce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/studiowebux%2Fbnaas-ce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/studiowebux%2Fbnaas-ce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/studiowebux%2Fbnaas-ce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/studiowebux","download_url":"https://codeload.github.com/studiowebux/bnaas-ce/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/studiowebux%2Fbnaas-ce/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282745088,"owners_count":26720200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-05T02:00:05.946Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude-code","cli","container","deno","http","linux-service","orchestration","pipeline","tasks","web-app","workflow"],"created_at":"2025-11-05T02:00:49.913Z","updated_at":"2025-11-05T02:02:07.713Z","avatar_url":"https://github.com/studiowebux.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Graph Execution Engine\n\nA powerful, flexible graph execution engine for Deno that supports HTTP requests, conditions, string interpolation, data manipulation, and custom functions. Perfect for building complex automation workflows, API orchestration, and data processing pipelines.\n\n## Features\n\n- **Graph-based execution flow** with conditional branching\n- **HTTP request execution** with full header and body support\n- **String interpolation** with AST-based expression parsing\n- **Data manipulation** with MongoDB-style queries (filter, sort, etc.)\n- **Variables system** for configuration management\n- **Before hooks** for state management and preparation\n- **Conditional routing** with complex boolean logic\n- **Nested property access** with dot notation and array indexing\n- **YAML and JSON** configuration support\n\n## Quick Start\n\n### Installation\n\n```bash\n# Clone the repository\ngit clone \u003crepository-url\u003e\ncd botnetasaservice\n```\n\n### Running the Graph Executor (CLI)\n\n```bash\n# Run a single graph configuration file\ndeno run --allow-net --allow-read src/runner/graph-executor.ts config.yml\n\n# With environment variables (for secrets)\nAPI_TOKEN=\"your-token\" USERNAME=\"user\" deno run --allow-net --allow-read src/runner/graph-executor.ts config.yml\n```\n\n### Running the Web UI \u0026 Orchestrator\n\nThe project includes a web-based UI for managing and monitoring graph executions:\n\n```bash\n# Start the main orchestrator server (includes API and UI)\ndeno task start\n# or\ndeno run -A src/server.ts\n\n# The server will start on http://localhost:3000\n# Default port can be changed with PORT environment variable\nPORT=8080 deno task start\n```\n\n### Environment Variables\n\n```bash\n# Server Configuration\nPORT=3000                           # API server port\nTASK_RUNNER_IMAGE=\"denoland/deno:2.4.4\"  # Docker image for task execution\nTASK_DEFAULT_MEMORY_LIMIT=\"128m\"    # Default memory limit for tasks\nTASK_DEFAULT_CPU_LIMIT=\"0.25\"       # Default CPU limit for tasks\nTASK_DEFAULT_TIMEOUT=\"1800000\"      # Default timeout (30 minutes)\n\n# Secrets (used in graph configurations)\nAPI_TOKEN=\"your-api-token\"\nUSERNAME=\"your-username\"\nPASSWORD=\"your-password\"\nDATABASE_URL=\"your-database-url\"\n```\n\n### Basic Example\n\nCreate a `config.yml` file:\n\n```yaml\nvariables:\n  API_VERSION: \"v1\"\n  BASE_URL: \"https://api.example.com\"\n\ninitialState:\n  users: []\n  results: []\n\nconfig:\n  verbose: true\n  http:\n    base_url: \"${var.BASE_URL}\"\n    headers:\n      \"Content-Type\": \"application/json\"\n      \"Authorization\": \"Bearer ${var.API_TOKEN}\"\n\nbefore:\n  start:\n    setup_logging:\n      executor:\n        type: \"none\"\n  all:\n    refresh_token:\n      executor:\n        type: \"http\"\n        method: \"GET\"\n        endpoint: \"/auth/refresh\"\n        mutate: \"auth\"\n\ngraph:\n  start:\n    description: \"Fetch user list\"\n    executor:\n      type: \"http\"\n      method: \"GET\"\n      endpoint: \"/api/${var.API_VERSION}/users\"\n      mutate: \"users\"\n    edges:\n      - to: \"process_users\"\n\n  process_users:\n    description: \"Filter and sort users\"\n    executor:\n      type: \"none\"\n    edges:\n      - to: \"end\"\n        condition:\n          path: \"users.filter({'active': true}).sort('name', 'asc').length\"\n          operator: \"\u003e\"\n          value: 0\n\n  end:\n    type: \"end\"\n    code: 0\n```\n\n## Configuration Structure\n\n### Root Configuration\n\n```yaml\nvariables: # Optional: Define reusable variables (clear text)\n  KEY: \"value\"\n\n# Secrets are provided via environment variables, not in config file\n# Use ${SECRET_NAME} to access them (e.g., ${API_TOKEN})\n\ninitialState: # Required: Initial state object\n  property: value\n\nconfig: # Required: Execution configuration\n  verbose: boolean\n  http:\n    base_url: string\n    headers: object\n\nbefore: # Optional: Before hooks\n  start: # Executed once at the beginning\n    hook_name:\n      executor: { ... }\n  all: # Executed before each condition evaluation\n    hook_name:\n      executor: { ... }\n\nconditions: # Optional: Named conditions\n  condition_name: { ... }\n\ngraph: # Required: Execution nodes\n  node_name:\n    description: string\n    executor: { ... }\n    edges: [...]\n```\n\n## Variables\n\nVariables provide a clean way to define reusable configuration values stored in **clear text**:\n\n```yaml\nvariables:\n  API_VERSION: \"v2\"\n  TIMEOUT: 5000\n  ENVIRONMENT: \"production\"\n\n# Usage in any string field:\nendpoint: \"/api/${var.API_VERSION}/users\"\nheaders:\n  \"X-Environment\": \"${var.ENVIRONMENT}\"\n  \"X-Timeout\": \"${var.TIMEOUT}\"\n```\n\n**Syntax:** `${var.VARIABLE_NAME}`\n\n## Secrets\n\nSecrets are designed for sensitive data and are **encrypted at rest** using AES-256-GCM encryption. The system provides secure secrets management with web UI access, and automatically injects only referenced secrets into task execution containers.\n\n### How Secrets Work\n\n1. **Encrypted Storage**: Secrets are encrypted using AES-256-GCM before storage in the database\n2. **Web UI Management**: Create, update, and manage secrets through the web interface\n3. **Smart Injection**: Only secrets referenced in task configurations are decrypted and passed to containers\n4. **Container Isolation**: Each task execution gets only the secrets it needs as environment variables\n\n### Secret Management\n\n#### Via Web UI\n\n1. Start the orchestrator: `deno task start`\n2. Open http://localhost:3000\n3. Navigate to the \"Secrets\" tab\n4. Create, update, or delete encrypted secrets\n\n#### Via API\n\n```bash\n# Create a secret\ncurl -X POST http://localhost:3000/api/secrets \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"API_TOKEN\",\n    \"value\": \"your-secret-token\",\n    \"description\": \"API authentication token\"\n  }'\n\n# List secrets (values are hidden)\ncurl http://localhost:3000/api/secrets\n```\n\n### Usage in Configuration\n\n**Syntax:** `${secret.SECRET_NAME}`\n\n```yaml\nvariables:\n  API_VERSION: \"v2\"\n  ENVIRONMENT: \"production\"\n\nconfig:\n  http:\n    base_url: \"https://api.example.com\"\n    headers:\n      \"Content-Type\": \"application/json\"\n      \"Authorization\": \"Bearer ${secret.API_TOKEN}\" # From environment\n      \"X-API-Version\": \"${var.API_VERSION}\" # From variables\n      \"X-Environment\": \"${var.ENVIRONMENT}\" # From variables\n\ngraph:\n  authenticate:\n    executor:\n      type: \"http\"\n      method: \"POST\"\n      endpoint: \"/auth/login\"\n      body:\n        username: \"${secret.USERNAME}\" # From environment\n        password: \"${secret.DATABASE_PASSWORD}\" # From environment\n        client_id: \"${var.CLIENT_ID}\" # From variables\n      mutate: \"auth\"\n\n  secure_upload:\n    executor:\n      type: \"http\"\n      method: \"POST\"\n      endpoint: \"/api/upload\"\n      headers:\n        \"X-Webhook-Secret\": \"${secret.WEBHOOK_SECRET}\" # From environment\n```\n\n### Variables vs Secrets\n\n| Feature        | Variables                 | Secrets               |\n| -------------- | ------------------------- | --------------------- |\n| **Storage**    | Clear text in config file | Encrypted in database |\n| **Syntax**     | `${var.NAME}`             | `${secret.NAME}`      |\n| **Management** | Config file editing       | Web UI / API          |\n| **Use Case**   | Configuration values      | Sensitive data        |\n| **Security**   | Visible in config         | AES-256-GCM encrypted |\n| **Injection**  | Always available          | Only when referenced  |\n| **Example**    | API version, timeouts     | API tokens, passwords |\n\n### Smart Secret Injection\n\nThe system automatically scans your task configurations for `${secret.NAME}` references and only injects the referenced secrets into the execution container. This provides several benefits:\n\n- **Security**: Only necessary secrets are exposed to each task\n- **Performance**: No overhead from unused secrets\n- **Audit Trail**: Clear tracking of which secrets are used by which tasks\n- **Isolation**: Each task gets its own isolated environment\n\n#### Development Workflow\n\n1. Create secrets via the web UI (http://localhost:3000)\n2. Reference secrets in your graph configurations using `${secret.NAME}`\n3. Run tasks - only referenced secrets are automatically injected\n\n### Agent-Specific Secret Mapping\n\nFor advanced use cases, you can map different secret values to the same variable name for different agents:\n\n```json\n{\n  \"agentId\": \"prod-agent\",\n  \"secretMapping\": {\n    \"API_TOKEN\": \"secret-id-for-prod-api\",\n    \"DB_PASSWORD\": \"secret-id-for-prod-db\"\n  }\n}\n```\n\nThis allows different agents to use different secret values for the same `${secret.API_TOKEN}` reference.\n\n### Security Features\n\n- **AES-256-GCM Encryption**: Industry-standard encryption for secret values at rest\n- **Smart Injection**: Only referenced secrets are decrypted and passed to containers\n- **Container Isolation**: Each task execution runs in an isolated container environment\n- **Audit Trail**: Track which secrets are used by which tasks\n- **Runtime Only**: Secrets are only available during task execution\n- **Zero Overhead**: Unused secrets don't impact performance\n\n### Best Practices\n\n- **Use variables** for non-sensitive configuration (API versions, URLs, timeouts, flags)\n- **Use secrets** for sensitive data (API tokens, passwords, keys, certificates)\n- **Rotate secrets regularly** through the web UI\n- **Use descriptive secret names** that clearly indicate their purpose\n- **Add descriptions and tags** to organize secrets effectively\n- **Monitor secret usage** through audit trails and logs\n- **Use agent-specific mappings** for different environments\n\n### Secret Validation\n\nThe system provides clear feedback during task execution:\n\n```\nWarning: Secret \"API_TOKEN\" not found in container environment\nFound 3 secret references: API_TOKEN, DB_PASSWORD, WEBHOOK_SECRET\nAdded secret \"API_TOKEN\" to container environment\n```\n\nThis helps identify missing or incorrectly configured secrets during development and testing.\n\n## Before Hooks\n\nBefore hooks allow you to execute code at specific times during execution:\n\n### Start Hooks\n\nExecuted **once** at the very beginning of graph execution:\n\n```yaml\nbefore:\n  start:\n    initialize_auth:\n      executor:\n        type: \"http\"\n        method: \"POST\"\n        endpoint: \"/auth/login\"\n        body:\n          username: \"${var.USERNAME}\"\n          password: \"${var.PASSWORD}\"\n        mutate: \"auth_token\"\n\n    setup_logging:\n      executor:\n        type: \"none\" # Setup logging configuration\n```\n\n### All Hooks\n\nExecuted **before each condition evaluation** to refresh state:\n\n```yaml\nbefore:\n  all:\n    refresh_data:\n      executor:\n        type: \"http\"\n        method: \"GET\"\n        endpoint: \"/api/status\"\n        mutate: \"status\"\n\n    update_timestamp:\n      executor:\n        type: \"none\" # Update current timestamp\n```\n\n### Legacy Support\n\nDirect hooks (backwards compatible) are treated as `all:` hooks:\n\n```yaml\nbefore:\n  refresh_token: # Treated as 'all:' hook\n    executor:\n      type: \"http\"\n      method: \"GET\"\n      endpoint: \"/auth/refresh\"\n```\n\n## Executors\n\n### HTTP Executor\n\n```yaml\nexecutor:\n  type: \"http\"\n  method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\"\n  endpoint: \"/api/path\"\n  body:                    # Optional: Request body\n    key: \"value\"\n  headers:                 # Optional: Additional headers\n    \"X-Custom\": \"value\"\n  mutate: \"state_key\"      # Optional: Store response in state\n```\n\n### Sleep Executor\n\n```yaml\nexecutor:\n  type: \"sleep\"\n  value: 1000 # Milliseconds\n```\n\n### None Executor\n\n```yaml\nexecutor:\n  type: \"none\" # No-op, useful for planning/comments\n```\n\n### End Executor\n\n```yaml\nexecutor:\n  type: \"end\"\n  code: 0 # Optional: Exit code (default: 0)\n```\n\n## String Interpolation\n\n### Variables: `${var.NAME}`\n\n```yaml\nendpoint: \"/api/${var.VERSION}/users\"\n```\n\n### Expressions: `{{ expression_or_state }}`\n\n\u003e Expressions and States\n\n```yaml\n# Simple property access\nendpoint: \"/users/{{ user.id }}/profile\"\n\n# Array access\nendpoint: \"/users/{{ users[0].id }}/profile\"\n\n# Function calls\nbody:\n  count: \"{{ max(10, users.length) }}\"\n\n# Complex expressions\nendpoint: \"/api/{{ users.find({active: true}).department }}/stats\"\n```\n\n### Data Manipulation Functions\n\n#### Filter\n\n```yaml\n# Basic filtering\n{{ users.filter({'active': true}) }}\n\n# Comparison operators\n{{ users.filter({'age': {'\u003e=': 18, '\u003c=': 65}}) }}\n\n# Multiple conditions\n{{ users.filter({'department': 'engineering', 'level': {'\u003e=': 3}}) }}\n\n# Nested properties\n{{ users.filter({'profile.experience': {'\u003e': 5}}) }}\n```\n\n**Supported operators:** `\u003c`, `\u003e`, `\u003c=`, `\u003e=`, `!=`, `==`, `$lt`, `$gt`, `$lte`, `$gte`, `$ne`, `$eq`\n\n#### Sort\n\nSort arrays by any field:\n\n```yaml\n# Ascending (default)\n{{ users.sort('name', 'asc') }}\n\n# Descending\n{{ users.sort('salary', 'desc') }}\n\n# Nested properties\n{{ users.sort('profile.joinDate', 'desc') }}\n```\n\n#### First and Last\n\nGet first or last items from arrays:\n\n```yaml\n# Get first user\n{{ users.filter({'active': true}).first.name }}\n\n# Get last user\n{{ users.sort('createdAt', 'desc').last.id }}\n\n# Chain operations\n{{ workers.filter({'salary': {'\u003c=': 100}}).sort('salary', 'asc').first.id }}\n```\n\n#### Find\n\nFind specific items in arrays:\n\n```yaml\n# Find by object\n{{ users.find({'id': 'user123'}) }}\n\n# Find by string (searches common properties)\n{{ users.find('john_doe') }}\n\n# Find with specific property\n{{ users.find('Manager', 'role') }}\n```\n\n## Conditions\n\n### Simple Conditions\n\n```yaml\ncondition:\n  path: \"users.length\"\n  operator: \"\u003e\"\n  value: 0\n```\n\n### Complex Conditions\n\n```yaml\n# AND condition\ncondition:\n  and:\n    - path: \"users.length\"\n      operator: \"\u003e\"\n      value: 0\n    - path: \"status.active\"\n      operator: \"=\"\n      value: true\n\n# OR condition\ncondition:\n  or:\n    - path: \"users.length\"\n      operator: \"\u003e\"\n      value: 10\n    - path: \"override\"\n      operator: \"=\"\n      value: true\n\n# Nested conditions\ncondition:\n  and:\n    - path: \"users.length\"\n      operator: \"\u003e\"\n      value: 0\n    - or:\n        - path: \"environment\"\n          operator: \"=\"\n          value: \"production\"\n        - path: \"force_run\"\n          operator: \"=\"\n          value: true\n```\n\n### Named Conditions\n\n```yaml\nconditions:\n  has_active_users:\n    path: \"users.filter({'active': true}).length\"\n    operator: \"\u003e\"\n    value: 0\n\n  is_business_hours:\n    and:\n      - path: \"current_hour\"\n        operator: \"\u003e=\"\n        value: 9\n      - path: \"current_hour\"\n        operator: \"\u003c\"\n        value: 17\n\ngraph:\n  start:\n    edges:\n      - to: \"process_users\"\n        condition: \"has_active_users\"\n      - to: \"wait\"\n        condition: \"is_business_hours\"\n        fallback: \"end\"\n```\n\n## Graph Nodes\n\n### Basic Node Structure\n\n```yaml\nnode_name:\n  description: \"Human readable description\"\n  executor:\n    type: \"http\"\n    # ... executor configuration\n  edges:\n    - to: \"next_node\"\n      condition: { ... } # Optional\n      fallback: \"other_node\" # Optional\n```\n\n### End Nodes\n\n```yaml\nsuccess_end:\n  type: \"end\"\n  code: 0\n\nerror_end:\n  type: \"end\"\n  code: 1\n```\n\n## Complete Example\n\n```yaml\nvariables:\n  API_BASE: \"https://api.company.com\"\n  API_VERSION: \"v2\"\n  DEPARTMENT: \"engineering\"\n\ninitialState:\n  employees: []\n  filtered_employees: []\n  stats: {}\n\nconfig:\n  verbose: true\n  http:\n    base_url: \"${var.API_BASE}\"\n    headers:\n      \"Content-Type\": \"application/json\"\n      \"X-API-Version\": \"${var.API_VERSION}\"\n\nbefore:\n  start:\n    authenticate:\n      executor:\n        type: \"http\"\n        method: \"POST\"\n        endpoint: \"/auth/login\"\n        body:\n          service: \"graph-executor\"\n        mutate: \"auth\"\n\n  all:\n    refresh_cache:\n      executor:\n        type: \"http\"\n        method: \"GET\"\n        endpoint: \"/cache/refresh\"\n\nconditions:\n  has_employees:\n    path: \"employees.length\"\n    operator: \"\u003e\"\n    value: 0\n\n  has_senior_employees:\n    path: \"employees.filter({'level': {'\u003e=': 5}}).length\"\n    operator: \"\u003e\"\n    value: 0\n\ngraph:\n  start:\n    description: \"Fetch all employees\"\n    executor:\n      type: \"http\"\n      method: \"GET\"\n      endpoint: \"/api/${var.API_VERSION}/employees\"\n      headers:\n        \"Authorization\": \"Bearer {{ auth.token }}\"\n      mutate: \"employees\"\n    edges:\n      - to: \"filter_employees\"\n        condition: \"has_employees\"\n      - to: \"no_employees\"\n\n  filter_employees:\n    description: \"Filter employees by department and experience\"\n    executor:\n      type: \"none\"\n    edges:\n      - to: \"calculate_stats\"\n        condition: \"has_senior_employees\"\n      - to: \"no_senior_staff\"\n\n  calculate_stats:\n    description: \"Calculate department statistics\"\n    executor:\n      type: \"http\"\n      method: \"POST\"\n      endpoint: \"/api/${var.API_VERSION}/stats\"\n      body:\n        department: \"${var.DEPARTMENT}\"\n        employees: \"{{ employees.filter({'department': var.DEPARTMENT, 'level': {'\u003e=': 5}}).sort('salary', 'desc') }}\"\n        count: \"{{ employees.filter({'department': var.DEPARTMENT}).length }}\"\n        average_salary: \"{{ employees.filter({'department': var.DEPARTMENT}).map(e =\u003e e.salary).reduce((a,b) =\u003e a+b, 0) / employees.filter({'department': var.DEPARTMENT}).length }}\"\n      mutate: \"stats\"\n    edges:\n      - to: \"success\"\n\n  no_employees:\n    description: \"Handle no employees case\"\n    executor:\n      type: \"none\"\n    edges:\n      - to: \"error_end\"\n\n  no_senior_staff:\n    description: \"Handle no senior staff case\"\n    executor:\n      type: \"none\"\n    edges:\n      - to: \"warning_end\"\n\n  success:\n    description: \"Success completion\"\n    executor:\n      type: \"none\"\n    edges:\n      - to: \"success_end\"\n\n  success_end:\n    type: \"end\"\n    code: 0\n\n  warning_end:\n    type: \"end\"\n    code: 2\n\n  error_end:\n    type: \"end\"\n    code: 1\n```\n\n## Advanced Usage\n\n### Chaining Data Operations\n\n```yaml\n# Complex data pipeline\nbody:\n  # Get high-performing employees\n  top_performers: \"{{ employees.filter({'performance_score': {'\u003e': 8.5}}).sort('performance_score', 'desc') }}\"\n\n  # Get department summary\n  department_summary: \"{{ employees.filter({'department': var.DEPARTMENT}).map(e =\u003e ({name: e.name, level: e.level, salary: e.salary})) }}\"\n\n  # Get first available manager\n  assigned_manager: \"{{ employees.filter({'role': 'manager', 'available': true}).first.id }}\"\n\n  # Statistics\n  total_count: \"{{ employees.length }}\"\n  active_count: \"{{ employees.filter({'active': true}).length }}\"\n  senior_count: \"{{ employees.filter({'level': {'\u003e=': 5}}).length }}\"\n```\n\n### Dynamic Endpoints\n\n```yaml\nexecutor:\n  type: \"http\"\n  method: \"GET\"\n  endpoint: \"/api/${var.API_VERSION}/departments/{{ user.department }}/employees/{{ user.filter({'active': true}).first.id }}\"\n```\n\n### Conditional Headers\n\n```yaml\nexecutor:\n  type: \"http\"\n  method: \"POST\"\n  endpoint: \"/api/data\"\n  headers:\n    \"Authorization\": \"Bearer {{ auth.token }}\"\n    \"X-Department\": \"{{ user.department || 'unknown' }}\"\n    \"X-User-Count\": \"{{ users.length }}\"\n```\n\n## Error Handling\n\n- **HTTP errors**: Execution stops and throws an error\n- **Missing variables**: Warning logged, original text preserved\n- **Invalid expressions**: Error logged, execution continues\n- **Missing nodes**: Execution stops and throws an error\n- **Before hook failures**: Warning logged, execution continues\n\n## Exit Codes\n\n- `0`: Success\n- `1`: Error (default for failures)\n- `2+`: Custom exit codes defined in end nodes\n\n## File Formats\n\nBoth YAML and JSON formats are supported:\n\n### YAML (Recommended)\n\n```yaml\nvariables:\n  KEY: \"value\"\ninitialState:\n  data: []\n# ... rest of configuration\n```\n\n### JSON\n\n```json\n{\n  \"variables\": {\n    \"KEY\": \"value\"\n  },\n  \"initialState\": {\n    \"data\": []\n  }\n}\n```\n\n## API Reference\n\n### Custom Functions\n\nAvailable in expressions (`{{ }}`):\n\n- `min(a, b)`: Return minimum value\n- `max(a, b)`: Return maximum value\n\n### Path Functions\n\nAvailable on arrays:\n\n- `filter(query)`: Filter array with MongoDB-style queries\n- `sort(key, direction)`: Sort array by key ('asc'/'desc')\n- `find(criteria, property?)`: Find item in array\n- `first`: Get first item (property access)\n- `last`: Get last item (property access)\n\n### Variable Access\n\n- `${var.VARIABLE_NAME}`: Access defined variables\n- `{{state}}`: Access defined states\n- `${secret.ENV_VAR}`: Access defined secret\n\n### Expression Syntax\n\n- Property access: `object.property`\n- Array access: `array[0]`, `object['key']`\n- Method calls: `array.filter({})`\n- Function calls: `max(a, b)`\n- Complex paths: `users.filter({}).sort().first.name`\n\n## License\n\nMIT License - see LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstudiowebux%2Fbnaas-ce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstudiowebux%2Fbnaas-ce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstudiowebux%2Fbnaas-ce/lists"}