feat(cli): flow-debug run-script — run a site script by name through the flow debugger
Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
@@ -32,6 +32,7 @@ public static class FlowDebugCommands
|
||||
var command = new Command("flow-debug") { Description = "Flow Debugger: trace an inbound API method or value-change flow end to end, or replay a past one" };
|
||||
|
||||
command.Add(BuildRunMethod(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildRunScript(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildRunValueChange(urlOption, formatOption, usernameOption, passwordOption));
|
||||
command.Add(BuildReplay(urlOption, formatOption, usernameOption, passwordOption));
|
||||
|
||||
@@ -82,13 +83,77 @@ public static class FlowDebugCommands
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private static Command BuildRunScript(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var instanceIdOption = new Option<int>("--instance-id") { Description = "Instance ID", Required = true };
|
||||
var scriptOption = new Option<string>("--script")
|
||||
{
|
||||
Description = "Canonical script name, as shown by the Flow Debugger picker / the flattened configuration (e.g. Manual or Module.Script)",
|
||||
Required = true,
|
||||
};
|
||||
var paramsOption = new Option<string?>("--params") { Description = "Flat JSON object of parameters for a Call/Interval/Expression/no-trigger script (validated against the script's ParameterDefinitions)" };
|
||||
var valueOption = new Option<string?>("--value") { Description = "Value to write to the trigger attribute of a ValueChange/Conditional script (canonical string form)" };
|
||||
var waitOption = new Option<int>("--wait-seconds") { Description = "Write kinds: how long to wait for the triggered run before NO_TRIGGER_WITHIN_WINDOW", DefaultValueFactory = _ => 45 };
|
||||
var timeoutOption = new Option<int>("--timeout-seconds")
|
||||
{
|
||||
Description = "Client-side wait for the run; raise above the script's ExecutionTimeoutSeconds + 25 for a long script",
|
||||
DefaultValueFactory = _ => 120,
|
||||
};
|
||||
var cmd = new Command("run-script") { Description = "Run one site script with tracing — a ValueChange/Conditional script is fired by writing its trigger attribute (--value); any other script is called directly (--params)" };
|
||||
cmd.Add(instanceIdOption);
|
||||
cmd.Add(scriptOption);
|
||||
cmd.Add(paramsOption);
|
||||
cmd.Add(valueOption);
|
||||
cmd.Add(waitOption);
|
||||
cmd.Add(timeoutOption);
|
||||
cmd.SetAction(async (ParseResult result) =>
|
||||
{
|
||||
var waitSeconds = result.GetValue(waitOption);
|
||||
var timeoutSeconds = result.GetValue(timeoutOption);
|
||||
if (waitSeconds <= 0)
|
||||
{
|
||||
OutputFormatter.WriteError("--wait-seconds must be greater than zero.", "INVALID_ARGUMENT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (timeoutSeconds <= 0)
|
||||
{
|
||||
OutputFormatter.WriteError("--timeout-seconds must be greater than zero.", "INVALID_ARGUMENT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
JsonElement? parameters;
|
||||
try
|
||||
{
|
||||
parameters = ParseParameters(result.GetValue(paramsOption));
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
OutputFormatter.WriteError($"--params is not valid JSON: {ex.Message}", "INVALID_PARAMS");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return await CommandHelpers.ExecuteCommandAsync(
|
||||
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new FlowDebugRunScriptCommand(
|
||||
result.GetValue(instanceIdOption),
|
||||
result.GetValue(scriptOption)!,
|
||||
parameters,
|
||||
result.GetValue(valueOption),
|
||||
waitSeconds),
|
||||
timeout: TimeSpan.FromSeconds(Math.Max(timeoutSeconds, waitSeconds + ClientSlackSeconds)),
|
||||
tableProjector: FlowDebugTableProjection.ProjectEvents);
|
||||
});
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private static Command BuildRunValueChange(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var instanceIdOption = new Option<int>("--instance-id") { Description = "Instance ID", Required = true };
|
||||
var attributeOption = new Option<string>("--attribute") { Description = "Canonical attribute name to write", Required = true };
|
||||
var valueOption = new Option<string>("--value") { Description = "Value to write (canonical string form)", Required = true };
|
||||
var waitOption = new Option<int>("--wait-seconds") { Description = "How long to wait for a triggered script run before reporting NO_TRIGGER_WITHIN_WINDOW", DefaultValueFactory = _ => 45 };
|
||||
var cmd = new Command("run-value-change") { Description = "Write an attribute through the traced path and capture the script run(s) the change triggers" };
|
||||
var cmd = new Command("run-value-change") { Description = "Write an attribute through the traced path and capture the script run(s) the change triggers — the attribute-level path (also fires Expression triggers); prefer run-script to run a named script" };
|
||||
cmd.Add(instanceIdOption);
|
||||
cmd.Add(attributeOption);
|
||||
cmd.Add(valueOption);
|
||||
|
||||
@@ -2270,16 +2270,18 @@ scadabridge --url <url> cached-call discard --site-id <string> --tracked-operati
|
||||
|
||||
---
|
||||
|
||||
### `flow-debug` — Trace an inbound API or value-change flow end to end
|
||||
### `flow-debug` — Trace an inbound API, script, or value-change flow end to end
|
||||
|
||||
The CLI face of the **Flow Debugger** (design `docs/plans/2026-09-05-flow-debugger-design.md`).
|
||||
A Live run executes the real thing — the method script on the active central node, or a
|
||||
real attribute write at the site — with tracing switched on for the whole chain, so every
|
||||
routed site script run, attribute write and attribute wait it causes is recorded as a
|
||||
`ScriptTrace` audit row and returned inline. Replay reads those rows back for any past
|
||||
execution. All three sub-commands require the **Deployer** role.
|
||||
The CLI face of the **Flow Debugger** (design `docs/plans/2026-09-05-flow-debugger-design.md`,
|
||||
script entry point `docs/plans/2026-09-08-flow-debugger-script-entry-design.md`).
|
||||
A Live run executes the real thing — the method script on the active central node, a named
|
||||
site script (called directly, or fired by writing its trigger attribute), or a real attribute
|
||||
write at the site — with tracing switched on for the whole chain, so every routed site script
|
||||
run, attribute write and attribute wait it causes is recorded as a `ScriptTrace` audit row and
|
||||
returned inline. Replay reads those rows back for any past execution. All four sub-commands
|
||||
require the **Deployer** role.
|
||||
|
||||
The two run commands are answered by the node the CLI is pointed at. Through the Traefik
|
||||
The three run commands are answered by the node the CLI is pointed at. Through the Traefik
|
||||
load balancer that is the active node; pointed at a standby node directly they refuse with
|
||||
error code `STANDBY_NODE` rather than run the script somewhere it would not run in
|
||||
production. `replay` is served from any node.
|
||||
@@ -2317,10 +2319,38 @@ scadabridge --url <url> flow-debug run-method --method-id <int> [--params <json>
|
||||
Parameters that fail schema validation are reported as `VALIDATION_FAILED` without running
|
||||
the script.
|
||||
|
||||
#### `flow-debug run-script`
|
||||
|
||||
Run one site script by its canonical name with tracing. A `ValueChange`/`Conditional` script
|
||||
is fired by writing its trigger attribute (`--value`) and waiting for the run it spawns,
|
||||
exactly like `run-value-change`; every other script (`Call`, `Interval`, `Expression`, or no
|
||||
trigger) is called directly with `--params`, the same way `run-method` calls an inbound API
|
||||
method. The runner resolves the script from the instance's flattened configuration and picks
|
||||
the branch itself — the client does not need to know the script's trigger kind ahead of time.
|
||||
|
||||
```sh
|
||||
scadabridge --url <url> flow-debug run-script --instance-id <int> --script <name> [--params <json>] [--value <string>] [--wait-seconds 45] [--timeout-seconds 120]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--instance-id` | yes | Instance ID |
|
||||
| `--script` | yes | Canonical script name, as shown by the Flow Debugger picker / the flattened configuration (e.g. `Manual` or `Module.Script`) |
|
||||
| `--params` | no | Flat JSON object of parameters for a Call/Interval/Expression/no-trigger script, validated against the script's `ParameterDefinitions` |
|
||||
| `--value` | no | Value to write to the trigger attribute of a ValueChange/Conditional script, in canonical string form |
|
||||
| `--wait-seconds` | no | Write kinds: how long to wait for the triggered run before reporting `NO_TRIGGER_WITHIN_WINDOW` (default 45) |
|
||||
| `--timeout-seconds` | no | Client-side wait for the run (default 120). Raise this above the script's own `ExecutionTimeoutSeconds + 25` for a long-running script. **Server ceiling:** the management endpoint applies `ScadaBridge:ManagementService:LongRunningCommandTimeout` (default 5 min) to `flow-debug run-script`, so a value above that still returns `504` at the server's ceiling — raise the server setting too if a script legitimately runs longer. |
|
||||
|
||||
A missing `--value` on a write-kind script, or `--params` that fail schema validation on a
|
||||
call-kind script, are reported as `VALIDATION_FAILED` without running the script. An unknown
|
||||
`--script` is reported as `NOT_FOUND`.
|
||||
|
||||
#### `flow-debug run-value-change`
|
||||
|
||||
Write an attribute on a deployed instance through the traced routed-write path, then wait
|
||||
for the script run(s) the change triggers at the site and print them.
|
||||
for the script run(s) the change triggers at the site and print them. This is the
|
||||
attribute-level path (it is also the way to fire an `Expression` trigger, which names no
|
||||
attribute of its own); prefer `run-script` to run a script by name.
|
||||
|
||||
```sh
|
||||
scadabridge --url <url> flow-debug run-value-change --instance-id <int> --attribute <name> --value <string> [--wait-seconds 45]
|
||||
|
||||
Reference in New Issue
Block a user