Dynamic Aliases
Dynamic aliases are script-defined commands. They appear alongside built-in
commands in the Tuoni UI and can validate input, queue child commands, combine
results, and update server-side data.
Alias contract
An alias implements four methods:
| Method |
Result |
Purpose |
configuration_schema() |
str |
Return raw JSON Schema text for the alias input |
can_send_to_agent(agent) |
bool |
Report whether the alias supports an agent |
validate_config(config, agent) |
None |
Raise tse.ValidationError for invalid input |
execute(ctx, config, agent) |
None |
Run the alias and finish or fail its context |
The same alias instance can execute concurrently. Do not store per-execution
state on the instance unless access to it is thread-safe.
| import tuoni_script_engine as tse
class MyAlias:
name = "my-alias"
def configuration_schema(self):
return """{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"required": []
}"""
def can_send_to_agent(self, agent):
return True
def validate_config(self, config, agent):
pass
def execute(self, ctx, config, agent):
ctx.set_result({"STDOUT": "Completed"})
ctx.finish()
tse.commands.register_dynamic_alias(MyAlias())
|
Register an instance, not the class. An alias without a name property can be
registered with an explicit name:
| tse.commands.register_dynamic_alias("my-alias", MyAlias())
|
Configuration schemas
configuration_schema() returns JSON Schema text. Tuoni also recognizes
files for named file inputs and positional for terminal arguments.
| def configuration_schema(self):
return """{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"target": {
"type": "string",
"description": "Target address"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
}
},
"required": ["target"],
"files": {
"payload": {}
},
"positional": {
"target": {"position": 0, "required": true},
"port": {"position": 1, "required": false}
}
}"""
|
The alias receives one of the public configuration types. JSON-only input is a
JsonConfiguration; input with named files is a MultipartConfiguration.
| def json_values(config):
if isinstance(config, tse.JsonConfiguration):
return config.to_dict()
if isinstance(config, tse.MultipartConfiguration) and config.json is not None:
return config.json.to_dict()
return {}
|
Use tse.ValidationError for expected validation failures:
| def validate_config(self, config, agent):
values = json_values(config)
if not values.get("target"):
raise tse.ValidationError("target is required")
|
Agent compatibility
Use typed properties and enums instead of string comparisons:
| def can_send_to_agent(self, agent):
return (
agent.status is tse.AgentStatus.ACTIVE
and agent.type is tse.AgentType.SHELLCODE_AGENT
and agent.metadata.operating_system is tse.OperatingSystem.MAC
and agent.metadata.os_architecture in {
tse.Architecture.X64,
tse.Architecture.ARM64,
}
)
|
Queueing commands
AliasContext.queue_command() accepts a command template name or object and an
explicit configuration:
| command = ctx.queue_command(
"sh",
tse.JsonConfiguration({"command": "id"}),
)
|
For a command with file input, use MultipartConfiguration:
| command = ctx.queue_command(
"upload",
tse.MultipartConfiguration(
json={"filepath": "/tmp/tool.bin"},
files={"file": b"file contents"},
),
)
|
Pass execution settings through the keyword-only exec_config argument:
| command = ctx.queue_command(
"portscan",
tse.JsonConfiguration({
"ips": ["10.0.0.1"],
"ports": [22, 443],
}),
exec_config=tse.SelfExecutionConfiguration(
exec_unit_type=tse.ExecUnitType.NATIVE_LIB,
),
)
|
Available execution configurations are:
SelfExecutionConfiguration
NewExecutionConfiguration
ExistingExecutionConfiguration
When exec_config is omitted, Tuoni chooses the normal agent context and a
compatible ExecUnit format.
Waiting for and reading results
wait_for_completion() returns False only when a finite timeout expires.
After a final result, inspect command.result.
| def execute(self, ctx, config, agent):
command = ctx.queue_command(
"sh",
tse.JsonConfiguration({"command": "whoami"}),
)
if not command.wait_for_completion(timeout=30):
command.cancel()
ctx.fail("The child command timed out")
return
if command.result is None:
ctx.fail("The child command returned no result")
return
if command.is_failed():
ctx.fail(command.result.error_message or "The child command failed")
return
ctx.set_result(command.result.entries)
ctx.finish()
|
Result entry values can be str, bytes, or tse.File. Reuse a returned file
directly in another multipart configuration:
| returned_file = next(
value
for value in command.result.entries.values()
if isinstance(value, tse.File)
)
upload = ctx.queue_command(
"upload",
tse.MultipartConfiguration(
json={"filepath": "/tmp/copied-file"},
files={"file": returned_file},
),
)
|
Completing an alias
Every execution path must call exactly one of:
ctx.finish() to complete successfully
ctx.fail(message) to complete with an error
ctx.set_result() may be called more than once to publish progress, but it does
not finish the alias.
| ctx.set_result({"status": "Halfway complete"})
ctx.set_result({"status": "Complete", "report": b"result bytes"})
ctx.finish()
|
Returning without finishing or failing leaves the alias ongoing.
Metadata properties and custom-property mappings are read-only views. Use the
provided update methods:
| properties = agent.metadata.custom_properties
notes = str(properties.get("notes", ""))
agent.metadata.update_custom_properties({
"notes": f"{notes};#reviewed" if notes else "#reviewed",
})
ctx.set_result({"STDOUT": "Agent tagged"})
ctx.finish()
|
Use replace_custom_properties() to replace all custom values and
remove_custom_property(name) to remove one.
Error handling
Raise tse.ValidationError only for expected input errors during validation.
Catch operational errors when the alias can add useful context; otherwise
Tuoni treats an exception from execute() as a failed parent command.
| def execute(self, ctx, config, agent):
try:
command = ctx.queue_command(
"ps",
tse.JsonConfiguration({}),
)
command.wait_for_completion()
if command.result is None or not command.is_success():
message = (
command.result.error_message
if command.result is not None
else "No result"
)
ctx.fail(message or "Process listing failed")
return
ctx.set_result(command.result.entries)
ctx.finish()
except tse.TuoniError as error:
ctx.fail(f"Tuoni API error: {error}")
|