feat(cli): operator aliases — pure bindings invoking stored queries (RFC-007 PR 3, part 2)

aliases: in the operator config bind a personal name to (server, graph,
stored-query NAME, positional arg mapping, fixed param defaults, format)
— zero content, per the ratified bindings-not-content model. Invocation
goes through the server's stored-query endpoint (POST
{base}/graphs/{g}/queries/{name}) with the keyed credential resolving via
the ordinary URL match; param precedence --params > positionals > fixed
defaults; the result renders through the existing format cascade with the
alias's format as its hop. A legacy omnigraph.yaml alias with the same
name wins during the RFC-008 window, with a warning naming both.

E2e (spawned policy-gated server, invoke_query granted via a per-graph
bundle): the alias invokes with name + one positional and nothing else —
server, graph, query, and token all from the operator layer; --server/
--graph explicit targeting; unknown --server lists defined names;
--server exclusive with a positional URI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
aaltshuler 2026-06-11 22:25:42 +03:00
parent 2b33ab64f2
commit dc91c55970
6 changed files with 256 additions and 6 deletions

View file

@ -746,6 +746,34 @@ async fn main() -> Result<()> {
}
let config = load_cli_config(config.as_ref())?;
// Operator aliases (RFC-007 PR 3): pure bindings to stored
// queries. A legacy file-alias with the same name wins during
// the RFC-008 window (with a warning); an alias name found
// only in the operator layer takes the invoke path here.
if let Some(alias_name) = alias.as_deref() {
let operator_config = crate::operator::load_operator_config()?;
if let Some(operator_alias) = operator_config.aliases.get(alias_name) {
if config.alias(alias_name).is_ok() {
eprintln!(
"warning: alias '{alias_name}' is defined in both omnigraph.yaml (legacy, wins during the deprecation window) and the operator config; the legacy definition applies"
);
} else {
let output = execute_operator_alias(
&http_client,
&config,
alias_name,
operator_alias,
&alias_args,
load_params_json(&params)?,
)
.await?;
let format =
resolve_read_format(&config, format, json, operator_alias.format);
print_read_output(&output, format, &config)?;
return Ok(());
}
}
}
let alias = resolve_alias(&config, alias.as_deref(), AliasCommand::Read)?;
let alias_name = alias.as_ref().map(|(name, _)| *name);
let alias_config = alias.as_ref().map(|(_, alias)| *alias);