> For the complete documentation index, see [llms.txt](https://adaptix-framework.gitbook.io/adaptix-framework/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://adaptix-framework.gitbook.io/adaptix-framework/development/extenders/service-extender.md).

# Service Extender

Service Extender — plugins for extending server functionality: notifications, integrations, automation.

### Flow

<figure><img src="/files/HHsnoydn4OKC6G8Qm1jf" alt=""><figcaption></figcaption></figure>

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                         SERVICE EXTENDER FLOW                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        INITIALIZATION                                 ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  Server Startup                                                             │
│         │                                                                   │
│         V                                                                   │
│  LoadPluginService(configPath)                                              │
│         │                                                                   │
│         ├──> Load config.yaml                                               │
│         │                                                                   │
│         ├──> Load plugin.so                                                 │
│         │                                                                   │
│         └──> InitPlugin(ts, moduleDir, serviceConfig)                       │
│                     │                                                       │
│                     ├──> [optional] Parse serviceConfig (YAML)              │
│                     │                                                       │
│                     ├──> [optional] Restore data from DB                    │
│                     │         TsExtenderDataLoad(extenderName, key)         │
│                     │                                                       │
│                     ├──> [optional] Register event hooks                    │
│                     │         TsEventHookRegister("agent.new", ...)         │
│                     │         TsEventHookRegister("agent.terminate", ...)   │
│                     │                                                       │
│                     ├──> [optional] Register HTTP endpoints                 │
│                     │         TsEndpointRegister("POST", "/api/...", ...)   │
│                     │                                                       │
│                     └──> return PluginService                               │
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        EVENT HANDLING                                 ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  Server Event (e.g. agent.new)                                              │
│         │                                                                   │
│         V                                                                   │
│  EventManager.Emit("agent.new", HookPost, eventData)                        │
│         │                                                                   │
│         V                                                                   │
│  Service Hook Handler                                                       │
│         │                                                                   │
│         ├──> Extract event data                                             │
│         │                                                                   │
│         ├──> Process (send notification, log, etc.)                         │
│         │                                                                   │
│         └──> return nil (success) or error                                  │
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        GUI INTERACTION                                ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  GUI Client ──> POST /service/call (via .axs ax.service_command)            │
│         │                                                                   │
│         V                                                                   │
│  TsServiceCall(serviceName, operator, function, args)                       │
│         │                                                                   │
│         V                                                                   │
│  PluginService.Call(operator, function, args)                               │
│         │                                                                   │
│         ├──> Process request                                                │
│         │                                                                   │
│         └──> TsServiceSendDataClient(operator, service, response)           │
│                     │                                                       │
│                     V                                                       │
│              GUI receives response via WebSocket                            │
│                                                                             │
│  ╔═══════════════════════════════════════════════════════════════════════╗  │
│  ║                        DATA PERSISTENCE                               ║  │
│  ╚═══════════════════════════════════════════════════════════════════════╝  │
│                                                                             │
│  Service needs to save/load config or data                                  │
│         │                                                                   │
│         ├──> TsExtenderDataSave(extenderName, key, value)                   │
│         │                                                                   │
│         └──> TsExtenderDataLoad(extenderName, key)                          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

### PluginService Interface

```go
type PluginService interface {
    // Call service function from GUI
    Call(operator string, function string, args string)
}
```

### AxScript for Service (ax\_config.axs)

The `.axs` file defines the UI for interacting with the service in GUI (settings dialogs, menus).

**Required functions:**

```javascript
// 1. Service initialization (called on load)
function InitService() {
    // Optional: initial setup
}

// 2. Data handler from server (called when receiving data via TsServiceSendData*)
function data_handler(data) {
    let response = JSON.parse(data);
    
    switch (response.action) {
        case "config":
            // Show configuration dialog
            buildConfigDialog(response);
            break;
        case "result":
            if (response.success) {
                ax.show_message("Success", "Operation completed!");
            } else {
                ax.show_message("Error", response.error);
            }
            break;
    }
}
```

**Optional elements:**

```javascript
// Service metadata
var metadata = {
    name: "My Service",
    description: "Service description"
};

// Add item to Settings menu
let settings_action = menu.create_action("My Service Settings", function() {
    // Request configuration from server
    ax.service_command("my_service", "get_config", {});
});
menu.add_main_settings(settings_action);

// Settings dialog example
function buildConfigDialog(config) {
    let label = form.create_label("API Key:");
    let text_key = form.create_textline(config.api_key || "");
    
    let check_enabled = form.create_check("Enable service");
    check_enabled.setChecked(config.enabled || false);
    
    let layout = form.create_vlayout();
    layout.addWidget(label);
    layout.addWidget(text_key);
    layout.addWidget(check_enabled);
    
    let dialog = form.create_dialog("Service Settings");
    dialog.setSize(400, 200);
    dialog.setLayout(layout);
    
    if (dialog.exec()) {
        // Send new configuration to server
        ax.service_command("my_service", "set_config", {
            api_key: text_key.text(),
            enabled: check_enabled.isChecked()
        });
    }
}
```

**GUI ↔ Server Interaction:**

```
GUI                              Server (Plugin)
 │                                    │
 ├────── ax.service_command ──────────┼──> PluginService.Call(operator, function, args)
 │    (service, function, args)       │
 │                                    │
 │                                    ├──> Process request
 │                                    │
 │<───────────────────────────────────┼── TsServiceSendDataClient(operator, service, data)
 │                                    │
 └─ data_handler(data) <──────────────┘
```

#### Service Config (config.yaml)

```yaml
extender_type: "service"
extender_file: "notifier.so"
ax_file: "ax_config.axs"

service_name: "notifier"
service_config: |
  bot_token: ""
  chat_ids: []
  enabled: false
  events:
    agent.new:
      enabled: true
      template: "*New Agent*\n└ ID: `{AgentId}`"
    agent.activate:
      enabled: true
      template: "*Agent Activated*\n└ ID: `{AgentId}`"
```

***

#### Example Teamserver API for Service Extender

```go
type Teamserver interface {
    // ═══════════════════════════════════════════════════════════════════════
    // EVENT HOOKS
    // ═══════════════════════════════════════════════════════════════════════
    
    // Register event handler
    TsEventHookRegister(eventType string, name string, phase int, priority int, handler func(event any) error) string
    
    // Remove handler by ID
    TsEventHookUnregister(hookID string) bool
    
    // Remove all handlers by name
    TsEventHookUnregisterByName(name string) int
    
    // Simplified Pre/Post hook registration
    TsEventHookOnPre(eventType string, name string, handler func(event any) error) string
    TsEventHookOnPost(eventType string, name string, handler func(event any) error) string
    
    // ═══════════════════════════════════════════════════════════════════════
    // GUI COMMUNICATION
    // ═══════════════════════════════════════════════════════════════════════
    
    // Send data to all clients
    TsServiceSendDataAll(service string, data string)
    
    // Send data to specific client
    TsServiceSendDataClient(operator string, service string, data string)
    
    // ═══════════════════════════════════════════════════════════════════════
    // DATA PERSISTENCE
    // ═══════════════════════════════════════════════════════════════════════
    
    // Save data to DB
    TsExtenderDataSave(extenderName string, key string, value []byte) error
    
    // Load data from DB
    TsExtenderDataLoad(extenderName string, key string) ([]byte, error)
    
    // Delete data
    TsExtenderDataDelete(extenderName string, key string) error
    
    // List keys
    TsExtenderDataKeys(extenderName string) ([]string, error)
    
    // Delete all data
    TsExtenderDataDeleteAll(extenderName string) error
    
    // ═══════════════════════════════════════════════════════════════════════
    // HTTP ENDPOINTS (optional)
    // ═══════════════════════════════════════════════════════════════════════
    
    // Register protected endpoint (with authentication)
    TsEndpointRegister(method string, path string, handler func(username string, body []byte) (int, []byte)) error
    
    // Register public endpoint (without authentication)
    TsEndpointRegisterPublic(method string, path string,  handler func(body []byte) (int, []byte)) error
    
    // Remove endpoints
    TsEndpointUnregister(method string, path string) error
    TsEndpointUnregisterPublic(method string, path string) error
    
    // Check existence
    TsEndpointExists(method string, path string) bool
    TsEndpointExistsPublic(method string, path string) bool
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://adaptix-framework.gitbook.io/adaptix-framework/development/extenders/service-extender.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
