Service Extender
Last updated
┌─────────────────────────────────────────────────────────────────────────────┐
│ 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) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘type PluginService interface {
// Call service function from GUI
Call(operator string, function string, args string)
}// 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;
}
}// 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 (Plugin)
│ │
├────── ax.service_command ──────────┼──> PluginService.Call(operator, function, args)
│ (service, function, args) │
│ │
│ ├──> Process request
│ │
│<───────────────────────────────────┼── TsServiceSendDataClient(operator, service, data)
│ │
└─ data_handler(data) <──────────────┘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}`"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
}