mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-27 02:39:38 +02:00
feat(cli): add read-only profile list / profile show (RFC-011 D8) (#255)
Inspect the per-operator `~/.omnigraph/config.yaml` scope profiles without running anything: - `profile list [--json]` — every profile with its binding (server/cluster/store) and default graph; marks the `$OMNIGRAPH_PROFILE`-active one. A malformed (zero/two-scope) profile is reported as `invalid: <reason>`, not a hard failure. - `profile show [<name>] [--json]` — one profile's resolved scope: binding kind + target, the resolved endpoint (a server's URL / a cluster's root / the store URI), default graph, and output format. With no name, shows the active (`$OMNIGRAPH_PROFILE`) profile, else the flat operator defaults. Both are `local` (Session plane) — they read operator config only, take no addressing flags. Display reads `OperatorProfile::binding()` + the same `servers`/`clusters` lookups the scope resolver uses (not `resolve_scope`, which is capability-gated and can't render all three binding kinds at once), so it is honest about what a profile binds. Also: RFC-011 bookkeeping (Status → Accepted; D8 shipped, D11 gated on RFC #219, D5 deferred) and drop the stale "legacy config actor (RFC-008 window)" comment in operator.rs (the legacy actor is gone). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6a2dfa7325
commit
7c092d3206
8 changed files with 333 additions and 8 deletions
|
|
@ -352,10 +352,32 @@ pub(crate) enum Command {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
},
|
},
|
||||||
|
/// Inspect the scope profiles in ~/.omnigraph/config.yaml (read-only).
|
||||||
|
Profile {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: ProfileCommand,
|
||||||
|
},
|
||||||
/// Print the CLI version
|
/// Print the CLI version
|
||||||
Version,
|
Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub(crate) enum ProfileCommand {
|
||||||
|
/// List the profiles defined in ~/.omnigraph/config.yaml.
|
||||||
|
List {
|
||||||
|
#[arg(long)]
|
||||||
|
json: bool,
|
||||||
|
},
|
||||||
|
/// Show a profile's resolved scope. With no name, shows the active
|
||||||
|
/// (`$OMNIGRAPH_PROFILE`) profile, else the flat operator defaults.
|
||||||
|
Show {
|
||||||
|
/// Profile name (optional).
|
||||||
|
name: Option<String>,
|
||||||
|
#[arg(long)]
|
||||||
|
json: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub(crate) enum ClusterCommand {
|
pub(crate) enum ClusterCommand {
|
||||||
/// Validate cluster.yaml and referenced schemas, queries, and policy files.
|
/// Validate cluster.yaml and referenced schemas, queries, and policy files.
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,95 @@ async fn main() -> Result<()> {
|
||||||
let path = crate::operator::remove_credential(&name)?;
|
let path = crate::operator::remove_credential(&name)?;
|
||||||
finish_logout(&name, &path, json)?;
|
finish_logout(&name, &path, json)?;
|
||||||
}
|
}
|
||||||
|
Command::Profile { command } => {
|
||||||
|
use crate::operator::ScopeBinding;
|
||||||
|
let op = crate::operator::load_operator_config()?;
|
||||||
|
let active = std::env::var(scope::PROFILE_ENV)
|
||||||
|
.ok()
|
||||||
|
.filter(|s| !s.is_empty());
|
||||||
|
match command {
|
||||||
|
ProfileCommand::List { json } => {
|
||||||
|
let items: Vec<ProfileListItem> = op
|
||||||
|
.profiles
|
||||||
|
.iter()
|
||||||
|
.map(|(name, profile)| {
|
||||||
|
let binding = match profile.binding(name) {
|
||||||
|
Ok(ScopeBinding::Server(s)) => format!("server: {s}"),
|
||||||
|
Ok(ScopeBinding::Cluster(c)) => format!("cluster: {c}"),
|
||||||
|
Ok(ScopeBinding::Store(u)) => format!("store: {u}"),
|
||||||
|
Err(e) => format!("invalid: {e}"),
|
||||||
|
};
|
||||||
|
ProfileListItem {
|
||||||
|
name: name.clone(),
|
||||||
|
binding,
|
||||||
|
default_graph: profile.default_graph.clone(),
|
||||||
|
active: active.as_deref() == Some(name.as_str()),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
print_profile_list(&items, json)?;
|
||||||
|
}
|
||||||
|
ProfileCommand::Show { name, json } => {
|
||||||
|
let detail = match name.or(active) {
|
||||||
|
Some(name) => {
|
||||||
|
let profile = op.profile(&name).ok_or_else(|| {
|
||||||
|
color_eyre::eyre::eyre!(
|
||||||
|
"unknown profile '{name}' (not defined under `profiles:`)"
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let (kind, target, endpoint) = match profile.binding(&name)? {
|
||||||
|
ScopeBinding::Server(s) => {
|
||||||
|
let endpoint = op.servers.get(&s).map(|sv| sv.url.clone());
|
||||||
|
("server", Some(s), endpoint)
|
||||||
|
}
|
||||||
|
ScopeBinding::Cluster(c) => {
|
||||||
|
let endpoint = op.cluster_root(&c).map(str::to_string);
|
||||||
|
("cluster", Some(c), endpoint)
|
||||||
|
}
|
||||||
|
ScopeBinding::Store(u) => ("store", Some(u.clone()), Some(u)),
|
||||||
|
};
|
||||||
|
ProfileDetail {
|
||||||
|
name,
|
||||||
|
scope_kind: kind.to_string(),
|
||||||
|
target,
|
||||||
|
endpoint,
|
||||||
|
default_graph: profile
|
||||||
|
.default_graph
|
||||||
|
.clone()
|
||||||
|
.or_else(|| op.default_graph().map(str::to_string)),
|
||||||
|
output_format: op
|
||||||
|
.output()
|
||||||
|
.and_then(|f| f.to_possible_value())
|
||||||
|
.map(|v| v.get_name().to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No name and no active profile: the flat operator defaults.
|
||||||
|
None => {
|
||||||
|
let (kind, target, endpoint) = if let Some(s) = op.default_server() {
|
||||||
|
let endpoint = op.servers.get(s).map(|sv| sv.url.clone());
|
||||||
|
("server", Some(s.to_string()), endpoint)
|
||||||
|
} else if let Some(u) = op.default_store() {
|
||||||
|
("store", Some(u.to_string()), Some(u.to_string()))
|
||||||
|
} else {
|
||||||
|
("none", None, None)
|
||||||
|
};
|
||||||
|
ProfileDetail {
|
||||||
|
name: "(defaults)".to_string(),
|
||||||
|
scope_kind: kind.to_string(),
|
||||||
|
target,
|
||||||
|
endpoint,
|
||||||
|
default_graph: op.default_graph().map(str::to_string),
|
||||||
|
output_format: op
|
||||||
|
.output()
|
||||||
|
.and_then(|f| f.to_possible_value())
|
||||||
|
.map(|v| v.get_name().to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
print_profile_detail(&detail, json)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Command::Version => {
|
Command::Version => {
|
||||||
println!("omnigraph {}", env!("CARGO_PKG_VERSION"));
|
println!("omnigraph {}", env!("CARGO_PKG_VERSION"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,7 @@ pub(crate) struct OperatorServer {
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Default, Deserialize)]
|
||||||
pub(crate) struct OperatorIdentity {
|
pub(crate) struct OperatorIdentity {
|
||||||
/// Default actor for every `--as` cascade (CLI direct-engine writes and
|
/// Default actor for every `--as` cascade (CLI direct-engine writes and
|
||||||
/// cluster commands alike): `--as` > legacy config actor (RFC-008
|
/// cluster commands alike): `--as` > this > none.
|
||||||
/// window) > this > none.
|
|
||||||
pub(crate) actor: Option<String>,
|
pub(crate) actor: Option<String>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
unknown: serde_yaml::Mapping,
|
unknown: serde_yaml::Mapping,
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,75 @@ pub(crate) fn finish_logout(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub(crate) struct ProfileListItem {
|
||||||
|
pub(crate) name: String,
|
||||||
|
/// `server: <n>` / `cluster: <n>` / `store: <uri>` / `invalid: <reason>`.
|
||||||
|
pub(crate) binding: String,
|
||||||
|
pub(crate) default_graph: Option<String>,
|
||||||
|
pub(crate) active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub(crate) struct ProfileDetail {
|
||||||
|
/// Profile name, or `(defaults)` for the no-name flat-defaults view.
|
||||||
|
pub(crate) name: String,
|
||||||
|
/// `server` | `cluster` | `store` | `none`.
|
||||||
|
pub(crate) scope_kind: String,
|
||||||
|
/// The bound server/cluster name, or the store URI.
|
||||||
|
pub(crate) target: Option<String>,
|
||||||
|
/// Resolved endpoint: a server's URL / a cluster's root / the store URI;
|
||||||
|
/// `None` if a named server/cluster isn't defined in this config.
|
||||||
|
pub(crate) endpoint: Option<String>,
|
||||||
|
pub(crate) default_graph: Option<String>,
|
||||||
|
pub(crate) output_format: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn print_profile_list(items: &[ProfileListItem], json: bool) -> Result<()> {
|
||||||
|
if json {
|
||||||
|
return print_json(&items);
|
||||||
|
}
|
||||||
|
if items.is_empty() {
|
||||||
|
println!("no profiles defined in the operator config");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
for item in items {
|
||||||
|
let active = if item.active { " (active)" } else { "" };
|
||||||
|
let graph = item
|
||||||
|
.default_graph
|
||||||
|
.as_deref()
|
||||||
|
.map(|g| format!(" · graph: {g}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
println!("{}{active} {}{graph}", item.name, item.binding);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn print_profile_detail(detail: &ProfileDetail, json: bool) -> Result<()> {
|
||||||
|
if json {
|
||||||
|
return print_json(detail);
|
||||||
|
}
|
||||||
|
println!("profile: {}", detail.name);
|
||||||
|
let target = detail
|
||||||
|
.target
|
||||||
|
.as_deref()
|
||||||
|
.map(|t| format!(" {t}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
println!(" scope: {}{target}", detail.scope_kind);
|
||||||
|
if let Some(endpoint) = &detail.endpoint {
|
||||||
|
println!(" endpoint: {endpoint}");
|
||||||
|
} else if matches!(detail.scope_kind.as_str(), "server" | "cluster") {
|
||||||
|
println!(" endpoint: (undefined — name not in this config)");
|
||||||
|
}
|
||||||
|
if let Some(graph) = &detail.default_graph {
|
||||||
|
println!(" default graph: {graph}");
|
||||||
|
}
|
||||||
|
if let Some(format) = &detail.output_format {
|
||||||
|
println!(" output: {format}");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Table prefs cascade (RFC-011): operator defaults.table_* > built-in.
|
/// Table prefs cascade (RFC-011): operator defaults.table_* > built-in.
|
||||||
pub(crate) fn resolve_table_render_options() -> ReadRenderOptions {
|
pub(crate) fn resolve_table_render_options() -> ReadRenderOptions {
|
||||||
let operator = crate::operator::load_operator_config().unwrap_or_default();
|
let operator = crate::operator::load_operator_config().unwrap_or_default();
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ pub(crate) fn command_plane(cmd: &Command) -> Plane {
|
||||||
Command::Embed(_)
|
Command::Embed(_)
|
||||||
| Command::Login { .. }
|
| Command::Login { .. }
|
||||||
| Command::Logout { .. }
|
| Command::Logout { .. }
|
||||||
|
| Command::Profile { .. }
|
||||||
| Command::Version => Plane::Session,
|
| Command::Version => Plane::Session,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +144,7 @@ pub(crate) fn command_label(cmd: &Command) -> &'static str {
|
||||||
Command::Version => "version",
|
Command::Version => "version",
|
||||||
Command::Login { .. } => "login",
|
Command::Login { .. } => "login",
|
||||||
Command::Logout { .. } => "logout",
|
Command::Logout { .. } => "logout",
|
||||||
|
Command::Profile { .. } => "profile",
|
||||||
Command::Embed(_) => "embed",
|
Command::Embed(_) => "embed",
|
||||||
Command::Init { .. } => "init",
|
Command::Init { .. } => "init",
|
||||||
Command::Load { .. } => "load",
|
Command::Load { .. } => "load",
|
||||||
|
|
|
||||||
|
|
@ -2069,3 +2069,143 @@ fn cli_fails_for_invalid_merge_requests() {
|
||||||
.contains("distinct source and target")
|
.contains("distinct source and target")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RFC-011 Decision 8: `profile list` / `profile show` inspect the operator
|
||||||
|
/// config's profiles read-only. Hermetic via OMNIGRAPH_HOME.
|
||||||
|
fn profile_home() -> tempfile::TempDir {
|
||||||
|
let home = tempdir().unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
home.path().join("config.yaml"),
|
||||||
|
"operator:\n actor: act-andrew\n\
|
||||||
|
defaults:\n output: json\n server: prod\n default_graph: knowledge\n\
|
||||||
|
servers:\n prod:\n url: https://graph.example.com\n\
|
||||||
|
clusters:\n brain:\n root: s3://acme/clusters/brain\n\
|
||||||
|
profiles:\n\
|
||||||
|
\x20 staging:\n server: prod\n default_graph: kb\n\
|
||||||
|
\x20 brain-admin:\n cluster: brain\n\
|
||||||
|
\x20 localdev:\n store: file:///data/dev.omni\n\
|
||||||
|
\x20 broken:\n server: a\n store: b\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
home
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_list_names_each_profile_with_its_binding_and_marks_active() {
|
||||||
|
let home = profile_home();
|
||||||
|
let out = output_success(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.env("OMNIGRAPH_PROFILE", "staging")
|
||||||
|
.arg("profile")
|
||||||
|
.arg("list"),
|
||||||
|
);
|
||||||
|
let stdout = stdout_string(&out);
|
||||||
|
assert!(stdout.contains("staging (active)"), "{stdout}");
|
||||||
|
assert!(stdout.contains("server: prod"), "{stdout}");
|
||||||
|
assert!(stdout.contains("cluster: brain"), "{stdout}");
|
||||||
|
assert!(stdout.contains("store: file:///data/dev.omni"), "{stdout}");
|
||||||
|
// A malformed (two-scope) profile is reported, not a hard failure.
|
||||||
|
assert!(stdout.contains("broken") && stdout.contains("invalid:"), "{stdout}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_list_json_shape() {
|
||||||
|
let home = profile_home();
|
||||||
|
let out = output_success(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.arg("profile")
|
||||||
|
.arg("list")
|
||||||
|
.arg("--json"),
|
||||||
|
);
|
||||||
|
let items: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||||
|
let brain = items
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|p| p["name"] == "brain-admin")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(brain["binding"], "cluster: brain");
|
||||||
|
assert_eq!(brain["active"], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_show_resolves_named_scope_endpoints() {
|
||||||
|
let home = profile_home();
|
||||||
|
// A cluster profile resolves its root.
|
||||||
|
let cluster = output_success(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.arg("profile")
|
||||||
|
.arg("show")
|
||||||
|
.arg("brain-admin"),
|
||||||
|
);
|
||||||
|
let cs = stdout_string(&cluster);
|
||||||
|
assert!(cs.contains("scope: cluster brain"), "{cs}");
|
||||||
|
assert!(cs.contains("endpoint: s3://acme/clusters/brain"), "{cs}");
|
||||||
|
|
||||||
|
// A store profile shows its URI as the endpoint.
|
||||||
|
let store = output_success(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.arg("profile")
|
||||||
|
.arg("show")
|
||||||
|
.arg("localdev")
|
||||||
|
.arg("--json"),
|
||||||
|
);
|
||||||
|
let detail: Value = serde_json::from_slice(&store.stdout).unwrap();
|
||||||
|
assert_eq!(detail["scope_kind"], "store");
|
||||||
|
assert_eq!(detail["endpoint"], "file:///data/dev.omni");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_show_without_name_falls_back_to_flat_defaults() {
|
||||||
|
let home = profile_home();
|
||||||
|
let out = output_success(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.arg("profile")
|
||||||
|
.arg("show")
|
||||||
|
.arg("--json"),
|
||||||
|
);
|
||||||
|
let detail: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||||
|
assert_eq!(detail["name"], "(defaults)");
|
||||||
|
assert_eq!(detail["scope_kind"], "server");
|
||||||
|
assert_eq!(detail["endpoint"], "https://graph.example.com");
|
||||||
|
assert_eq!(detail["default_graph"], "knowledge");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_show_without_name_uses_active_env_profile() {
|
||||||
|
let home = profile_home();
|
||||||
|
let out = output_success(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.env("OMNIGRAPH_PROFILE", "brain-admin")
|
||||||
|
.arg("profile")
|
||||||
|
.arg("show")
|
||||||
|
.arg("--json"),
|
||||||
|
);
|
||||||
|
let detail: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||||
|
// No name arg, but $OMNIGRAPH_PROFILE selects brain-admin (not the flat defaults).
|
||||||
|
assert_eq!(detail["name"], "brain-admin");
|
||||||
|
assert_eq!(detail["scope_kind"], "cluster");
|
||||||
|
assert_eq!(detail["endpoint"], "s3://acme/clusters/brain");
|
||||||
|
// output_format renders as the canonical lowercase value name.
|
||||||
|
assert_eq!(detail["output_format"], "json");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_show_unknown_name_errors() {
|
||||||
|
let home = profile_home();
|
||||||
|
let out = output_failure(
|
||||||
|
cli()
|
||||||
|
.env("OMNIGRAPH_HOME", home.path())
|
||||||
|
.arg("profile")
|
||||||
|
.arg("show")
|
||||||
|
.arg("nope"),
|
||||||
|
);
|
||||||
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||||
|
assert!(stderr.contains("unknown profile 'nope'"), "{stderr}");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
# RFC-011: CLI refactoring — one addressing & config model
|
# RFC-011: CLI refactoring — one addressing & config model
|
||||||
|
|
||||||
**Status:** Proposed
|
**Status:** Accepted — implemented (the `omnigraph.yaml` excision landed as
|
||||||
|
#250/#251/#252; D1–D4, D6, D7, D9, D10 shipped). Two items remain: **D11**
|
||||||
|
(server-side maintenance jobs) is gated on the bulk-data-plane RFC #219; **D5**
|
||||||
|
(combined admin scope) stays deferred by design.
|
||||||
**Date:** 2026-06-14
|
**Date:** 2026-06-14
|
||||||
**Audience:** CLI/server maintainers
|
**Audience:** CLI/server maintainers
|
||||||
**Builds on:** [rfc-007-operator-config.md](rfc-007-operator-config.md)
|
**Builds on:** [rfc-007-operator-config.md](rfc-007-operator-config.md)
|
||||||
|
|
@ -526,10 +529,9 @@ Non-blocking; settle when convenient.
|
||||||
server scope and maintain via `--cluster`. A `deployments: { … }` object
|
server scope and maintain via `--cluster`. A `deployments: { … }` object
|
||||||
(server + cluster validated coherent, referenced by a profile) is revisited only
|
(server + cluster validated coherent, referenced by a profile) is revisited only
|
||||||
if admin ergonomics demand it — and Decision 11 largely removes the need.
|
if admin ergonomics demand it — and Decision 11 largely removes the need.
|
||||||
- **D8 — the `profile` command surface.** `profile list` / `profile show`
|
- **D8 — the `profile` command surface.** *Shipped:* `profile list` / `profile
|
||||||
(read-only inspection) are additive diagnostics, shippable anytime; they don't
|
show [<name>]` (read-only inspection). The *no sticky `profile use`* constraint
|
||||||
touch the grammar or resolution. The *no sticky `profile use`* constraint holds
|
holds — it is a design principle, not a command.
|
||||||
regardless — it is a design principle, not a command.
|
|
||||||
|
|
||||||
## Safety
|
## Safety
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ Top-level command families and subcommands. Graph-targeting commands accept a po
|
||||||
| `cleanup --keep N --older-than 7d --confirm` | destructive version GC (`--confirm` to execute; also needs `--yes` against a non-local `s3://` target — see *Write diagnostics & destructive confirmation*) |
|
| `cleanup --keep N --older-than 7d --confirm` | destructive version GC (`--confirm` to execute; also needs `--yes` against a non-local `s3://` target — see *Write diagnostics & destructive confirmation*) |
|
||||||
| `embed` | offline JSONL embedding pipeline |
|
| `embed` | offline JSONL embedding pipeline |
|
||||||
| `policy validate \| test \| explain` | Cedar tooling against a cluster's applied policies (`--cluster <dir>`; `--graph <id>` picks a graph's bundle when several apply). `test` takes `--tests <file>`; `explain` takes `--actor`/`--action`/`--branch`/`--target-branch` |
|
| `policy validate \| test \| explain` | Cedar tooling against a cluster's applied policies (`--cluster <dir>`; `--graph <id>` picks a graph's bundle when several apply). `test` takes `--tests <file>`; `explain` takes `--actor`/`--action`/`--branch`/`--target-branch` |
|
||||||
|
| `profile list \| show [<name>]` | read-only inspection of `~/.omnigraph/config.yaml` profiles. `list` shows each profile's binding (server/cluster/store) + default graph and marks the `$OMNIGRAPH_PROFILE`-active one; `show` resolves one profile's scope (endpoint + default graph), defaulting to the active profile, else the flat operator defaults |
|
||||||
| `version` / `-v` | print `omnigraph 0.3.x` |
|
| `version` / `-v` | print `omnigraph 0.3.x` |
|
||||||
|
|
||||||
## Command capabilities
|
## Command capabilities
|
||||||
|
|
@ -106,7 +107,8 @@ address (a positional URI, `--server`, or `--store <uri>`); a named
|
||||||
*local* default — mutually exclusive with `defaults.server`). A **profile** binds
|
*local* default — mutually exclusive with `defaults.server`). A **profile** binds
|
||||||
exactly one of `server` / `cluster` / `store` plus an optional default graph —
|
exactly one of `server` / `cluster` / `store` plus an optional default graph —
|
||||||
config data, not state: every command resolves its scope fresh, there is no
|
config data, not state: every command resolves its scope fresh, there is no
|
||||||
sticky "current" mode.
|
sticky "current" mode. Inspect what is defined with `omnigraph profile list` and
|
||||||
|
`omnigraph profile show [<name>]` (read-only).
|
||||||
|
|
||||||
- `--store <uri>` addresses a single graph's storage directly (ad-hoc / break-glass).
|
- `--store <uri>` addresses a single graph's storage directly (ad-hoc / break-glass).
|
||||||
- A `cluster`-bound profile reaches `optimize` / `repair` / `cleanup` for a managed
|
- A `cluster`-bound profile reaches `optimize` / `repair` / `cleanup` for a managed
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue