mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-18 02:24:27 +02:00
* feat(cli): --server accepts a literal URL (RFC-011 Decision 2) `resolve_server_flag` now treats a `--server` value containing `://` as a literal base URL (trailing slash trimmed; `--graph` appends `/graphs/<id>`), bypassing the operator-config `servers:` registry; a bare name still resolves through the registry. This is the replacement the upcoming `--uri http(s)://` deprecation points at, and a small ergonomic win on its own (`--server https://host` with no config entry). Token resolution for a literal-URL server falls to the legacy OMNIGRAPH_BEARER_TOKEN chain, same as a positional URL today. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(cli): address the parity-matrix arms with global --store/--server flags Prep for removing the positional-http→remote dispatch. The parity harness addressed both arms with a positional graph right after the verb (`omnigraph <verb> <addr> <args…>`), which only parses for top-level verbs — for nested subcommands (`schema show`, `branch list`, …) the address landed in the subcommand slot and BOTH arms failed identically, so the test passed vacuously (matching exit codes, never comparing output). Address both arms with the global flags instead — local `--store <graph>` (embedded), remote `--server <url>` (served) — appended after the verb + args, valid regardless of nesting. The previously-vacuous nested-verb parity checks now actually compare embedded vs remote (and pass — parity holds), and the remote arm no longer relies on the positional-URL dispatch that's about to be removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(cli)!: --as on a served write is a hard error (was a silent no-op) A served write resolves the actor server-side from the bearer token, so `--as` could never set identity there — it was silently ignored. It now errors (in the remote write factory, before any HTTP call), pointing the user at removing `--as` or writing directly with `--store`. Reads don't carry `--as`, so this is write-path only. BREAKING for any script that passed `--as` to a remote write (it was a no-op, so behavior is unchanged except the now-explicit error). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(cli)!: a positional/--uri http(s):// URL no longer dispatches to a server Remote graphs must be addressed with `--server <url>` (or a named server / a profile binding one). A positional or `--uri` `http(s)://` URL on a data verb now errors instead of silently routing to the remote HTTP client — the scheme no longer carries transport semantics. The discriminator is `via_server`: a remote URL produced by a server scope is fine; a remote URL from a positional/`--uri` source is rejected (`reject_positional_remote` in both GraphClient factories). Storage verbs are unaffected — they already reject remote URIs through `resolve_local_graph` with the existing "direct (storage-native)" error. Migrated the gh-host keyed-credential system test to `--server <url>` (the literal URL still prefix-matches the operator server for token resolution). BREAKING: scripts addressing a server by a bare URL must switch to `--server <url>`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(cli)!: remove the --target flag (use --store / --profile / --server) Removes the legacy named-graph flag and threads its parameter out of the whole resolver chain. `--target` resolved a graph name through `omnigraph.yaml`'s `graphs:` map; its replacements (`--store <uri>`, `--profile <name>`, `--server <name>`) all ship. - Drops the 22 `target` clap fields + the `--cluster` exclusion that named it. - Threads `target`/`cli_target` out of `resolve_uri`/`resolve_cli_graph`/ `resolve_local_graph`/`resolve_local_uri`/`resolve_storage_uri`/ `resolve_remote_bearer_token`/`apply_server_flag`/`execute_query_lint`/ `resolve_selected_graph`/`resolve_registry_selection_for_list`/ `execute_queries_{validate,list}`, the two `GraphClient` factories, and `ScopeFlags`/`ResolvedScope`. - Keeps the shared `OmnigraphConfig::resolve_target_uri` 3-arg (server boot uses it); the CLI passes None for the explicit-target arm. The `cli.graph` default (omnigraph.yaml bare-command fallback) is unchanged — its removal belongs to the omnigraph.yaml excision. - Operator/file aliases that bind a `graph` name still work: the name is now resolved to a URI inline (a positional URI wins). - Error messages and `--graph`/`--server`/`--store` help text no longer name `--target`; the queries-list selection hint points at `cli.graph`. BREAKING. Tests updated (named-target resolution rewritten onto `cli.graph`; positional-URI tests unchanged). Full omnigraph-cli suite green (228). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(cli): drop --target and positional-http addressing; --as-on-served is an error Update the user docs for the legacy data-plane addressing removals: - the CLI `--target` flag is gone — address graphs with a positional URI, `--store`, `--profile`, or `--server <name|url>`; - a positional `http(s)://` URI no longer dispatches to a server (use `--server`); - `--as` on a served write is now rejected (was a silent no-op). Touches cli/reference.md (addressing intro, capability table, error examples, scopes), cli/index.md (the remote-read example → --server), operations/maintenance + policy, and the cluster docs' data-plane load guidance. The server's own `--target` boot flag is unchanged (server.md untouched). Also fixes a pre-existing broken maintenance link in search/indexes.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): --store is loudly exclusive with a positional URI / --server; test graphs→Served Address two Greptile findings on the RFC-011 slices: - Slice A (P1): `--store` combined with a positional URI silently dropped the URI (`scope.rs` did `store.or(uri)`); `--store` + `--server` errored with a misleading "positional URI" message. Now both combinations fail loudly with a declared `--store is exclusive with a positional URI and --server` error. - Slice B (P2): the `command_capability` unit test never exercised the one Data→Served refinement (`graphs`); added the assertion so deleting that guard can't pass silently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
261 lines
17 KiB
Markdown
261 lines
17 KiB
Markdown
# CLI Reference (`omnigraph`)
|
|
|
|
A reference for the `omnigraph` binary's command surface and `omnigraph.yaml` schema. For a quick-start guide, see [cli.md](index.md).
|
|
|
|
Top-level command families and subcommands. Graph-targeting commands accept a positional `file://`/`s3://` URI, `--server <name|url>` (an operator-defined server from `~/.omnigraph/config.yaml` by name, or a literal `http(s)://` URL, optionally with `--graph <id>` for multi-graph servers; exclusive with a positional URI), `--store <uri>` (a single graph's storage directly), or `--profile <name>` / `$OMNIGRAPH_PROFILE` (a named scope bundle; see [Scopes & profiles](#scopes--profiles-rfc-011)); `cluster` commands use `--config <dir>`. A remote server is addressed only with `--server` — a positional `http(s)://` URI is rejected.
|
|
|
|
## Top-level commands
|
|
|
|
| Command | Purpose |
|
|
|---|---|
|
|
| `init` | `--schema <pg>` → initialize a graph (no longer scaffolds `omnigraph.yaml`; start cluster configs from the [cluster.md](../clusters/index.md) quick-start or `config migrate`) |
|
|
| `load` | bulk load a branch, local or remote (`--mode overwrite\|append\|merge` is **required** — overwrite is destructive, so there is no default). Without `--from` the target branch must exist; `--from <base>` forks a missing `--branch` from `<base>` first |
|
|
| `ingest` | deprecated alias of `load --from <base>` (defaults: `--from main --mode merge`); prints a one-line warning to stderr |
|
|
| `query` (alias: `read`) | run named read query; source via `--query <path>`, `-e`/`--query-string <GQ>`, or `--alias <name>` (exactly one). `read` is the deprecated previous name and prints a one-line warning to stderr |
|
|
| `mutate` (alias: `change`) | run mutation query; same `--query` / `-e` / `--alias` mutual-exclusion as `query`. `change` is the deprecated previous name and prints a one-line warning to stderr |
|
|
| `snapshot` | print current snapshot (per-table version + row count) |
|
|
| `export` | dump to JSONL on stdout (`--type T`, `--table K` filters) |
|
|
| `branch create \| list \| delete \| merge` | branching ops |
|
|
| `commit list \| show` | inspect commit graph |
|
|
| `schema plan \| apply \| show (alias: get)` | migrations |
|
|
| `lint` (alias: `check`) | offline / graph-backed query validation. Replaces `query lint` / `query check`, which are kept as deprecated argv-level shims that print a one-line warning and rewrite to `omnigraph lint` |
|
|
| `config migrate` | propose (or `--write`: apply) the split of a legacy `omnigraph.yaml` — team half → ready-to-review `cluster.yaml`, personal half → `~/.omnigraph/config.yaml` (key-level merge, existing entries win), plus dropped-key reasons and manual steps |
|
|
| `cluster validate \| plan \| apply \| approve \| status \| refresh \| import \| force-unlock` | declarative cluster control plane. `validate` checks a local `cluster.yaml` folder and referenced schema/query/policy files; `plan` diffs it against local JSON state at `__cluster/state.json`, annotates dispositions, and embeds real schema-migration previews; `apply` converges the cluster — stored-query/policy catalog writes (content-addressed under `__cluster/resources/`), graph creates, schema updates (soft drops only; `--as` records the actor), and graph deletes behind a digest-bound approval from `cluster approve <resource> --as <actor>` (`apply`/`approve` default the actor from the per-operator `omnigraph.yaml`'s `cli.actor` when `--as` is omitted; nothing else in that file affects cluster commands); what apply converges is what an `omnigraph-server --cluster <dir>` deployment serves on its next restart (omnigraph.yaml deployments are unaffected); `status` reads the state ledger; `refresh`/`import` explicitly update local JSON state from read-only graph observations; `force-unlock <LOCK_ID>` manually removes a held local state lock by exact id |
|
|
| `optimize` | non-destructive Lance compaction (skips tables with `Blob` columns or uncovered drift; `--json` reports `skipped`) |
|
|
| `repair [--confirm] [--force]` | preview or explicitly publish uncovered manifest/head drift. `--confirm` heals verified maintenance drift and exits non-zero if suspicious/unverifiable drift is refused; `--force --confirm` publishes suspicious/unverifiable drift after operator review |
|
|
| `cleanup --keep N --older-than 7d --confirm` | destructive version GC |
|
|
| `embed` | offline JSONL embedding pipeline |
|
|
| `policy validate \| test \| explain` | Cedar tooling. Selects `cli.graph`, else `server.graph`, else top-level `policy.file` |
|
|
| `version` / `-v` | print `omnigraph 0.3.x` |
|
|
|
|
## Command capabilities
|
|
|
|
Every command declares the **capability** it needs — what it requires to reach a graph — which determines the addressing flags that apply:
|
|
|
|
- **`any`** — `query`, `mutate`, `load`, `ingest`, `branch *`, `snapshot`, `export`, `commit *`, `schema show`, `schema apply`. Run against a graph **served (via a server) or embedded (direct against a store)**: accept a positional `file://`/`s3://` URI, `--server <name|url>` (+ `--graph <id>` for multi-graph servers), `--store <uri>`, or `--profile <name>`. A remote server is addressed with `--server` — a positional `http(s)://` URI does **not** dispatch to one.
|
|
- **`served`** — `graphs list`. Requires a server (accepts `--server` / `--profile`).
|
|
- **`direct`** — `init`, `optimize`, `repair`, `cleanup`, `schema plan`, `queries validate`, `lint`. Need **direct storage access** (`file://` / `s3://`), never through a server. They accept a positional `URI`, but **not** `--server` / `--graph`, and a remote (`http(s)://`) URI is rejected. `optimize` / `repair` / `cleanup` also accept **`--cluster <dir|s3://…> --cluster-graph <id>`**, which resolves the graph's storage URI from the served cluster state (so you needn't know the `<storage>/graphs/<id>.omni` layout).
|
|
- **`control`** — `cluster *`. Operates on a cluster directory via `--config <dir>`.
|
|
- **`local`** — `policy *`, `embed`, `login`, `logout`, `config`, `version`, `queries list`. Address no graph.
|
|
|
|
These restrictions are enforced and reported, not silent:
|
|
|
|
- A served-graph flag (`--server` / `--graph`) on a verb that doesn't reach a graph through a server fails loudly, e.g.: ``optimize is a direct (storage-native) command; --server/--graph address a served graph and do not apply. Pass a storage URI, or --cluster <dir> --cluster-graph <id>.``
|
|
- A `direct` verb pointed at a remote URI fails loudly, e.g.: ``optimize is a direct (storage-native) command and needs direct storage access; the resolved target is a remote server (https://…). Pass the graph's file:// or s3:// URI.``
|
|
- A data verb pointed at a positional `http(s)://` URI fails loudly: ``a remote graph must be addressed with --server <url> — a positional (or --uri) http(s):// URL no longer dispatches to a server.``
|
|
- `init` into an **established cluster's** storage layout (`<root>/graphs/<id>.omni` where `<root>` holds `__cluster/state.json`) is refused — graphs in a cluster are created by `cluster apply` (which records ledger / recovery / approvals), not `init`.
|
|
|
|
To maintain a server-backed graph, run the `direct` verbs from a host with storage access against the graph's storage URI (a positional URI, or `--cluster … --cluster-graph …`), out-of-band from the serving process — there are no server routes for `optimize` / `repair` / `cleanup` by design.
|
|
|
|
`omnigraph --help` lists commands with a **capability legend** at the bottom (any / served / direct / control / local).
|
|
|
|
## Config surfaces
|
|
|
|
Two config surfaces with single owners, plus a zero-config tier:
|
|
|
|
| Surface | Owner | Location | Declares |
|
|
|---|---|---|---|
|
|
| Cluster config | the team, in a repo | `cluster.yaml` + checkout ([cluster-config.md](../clusters/config.md)) | what the system **is**: graphs, schemas, queries, policies, storage |
|
|
| Operator config | one person | `~/.omnigraph/config.yaml` (override dir with `$OMNIGRAPH_HOME`) | who **I** am: identity, ergonomics |
|
|
| Flags / env | per invocation | — | everything, explicitly |
|
|
|
|
`omnigraph.yaml` (below) is the legacy combined file — fully supported
|
|
today, slated for staged deprecation; its keys' future homes are
|
|
listed there.
|
|
|
|
### `~/.omnigraph/config.yaml` (operator)
|
|
|
|
```yaml
|
|
operator:
|
|
actor: act-andrew # default identity for every --as cascade:
|
|
# --as > legacy cli.actor > operator.actor > none
|
|
servers: # operator-owned endpoints; names key the credentials
|
|
prod:
|
|
url: https://graph.example.com # no tokens in this file, ever
|
|
defaults:
|
|
output: table # read format default, below --json/--format/alias/legacy
|
|
server: prod # the everyday scope when no address is given (RFC-011)
|
|
default_graph: knowledge # graph selected in a server/cluster scope
|
|
clusters: # admin-only: managed-cluster storage roots (RFC-011).
|
|
brain: # the ONLY place a storage root lives in this file.
|
|
root: s3://acme/clusters/brain
|
|
profiles: # named scope bundles (RFC-011); pick with --profile
|
|
staging: { server: staging, default_graph: knowledge } # a served scope
|
|
brain-admin: { cluster: brain, default_graph: knowledge } # a direct cluster scope
|
|
```
|
|
|
|
Absent file = empty layer. Unknown keys warn and load (a file written for a
|
|
newer CLI works on an older one). `$OMNIGRAPH_CONFIG=<path>` stands in for
|
|
`--config` (the flag wins) in both the CLI and the server.
|
|
|
|
#### Scopes & profiles (RFC-011)
|
|
|
|
A command resolves a **scope** — a server, a cluster, or a store — then selects a
|
|
graph in it; the served-vs-direct access path is derived from the scope, not
|
|
toggled. The scope comes from one of (highest precedence first): an explicit
|
|
address (a positional URI, `--server`, or `--store <uri>`); a named
|
|
`--profile <name>` (or `$OMNIGRAPH_PROFILE`); or the flat `defaults.server` +
|
|
`defaults.default_graph`. A **profile** binds exactly one of `server` / `cluster`
|
|
/ `store` plus an optional default graph — config data, not state: every command
|
|
resolves its scope fresh, there is no sticky "current" mode.
|
|
|
|
- `--store <uri>` addresses a single graph's storage directly (ad-hoc / break-glass).
|
|
- A `cluster`-bound profile reaches `optimize` / `repair` / `cleanup` for a managed
|
|
graph (resolving its storage root from `clusters:`), the same as
|
|
`--cluster <root> --cluster-graph <id>`.
|
|
- A `server`-bound scope on a maintenance verb, or a `cluster`-bound scope on a
|
|
data verb, is rejected with a message pointing at the right addressing.
|
|
|
|
`--target` and the positional-`http(s)://`→remote dispatch have been **removed**;
|
|
the remaining legacy surfaces (`--cluster-graph`, `omnigraph.yaml`'s `cli.graph`
|
|
default) still work and an explicit address always wins.
|
|
|
|
#### Credentials keyed by server name
|
|
|
|
`omnigraph login <name>` stores a bearer token in
|
|
`~/.omnigraph/credentials` (created `0600`; group/world-readable files are
|
|
refused). Token from `--token`, or — preferred, keeps it out of shell
|
|
history — one line on stdin: `echo $TOKEN | omnigraph login prod`.
|
|
`omnigraph logout <name>` removes it (idempotent).
|
|
|
|
#### Operator aliases — bindings, not content
|
|
|
|
An operator alias is a personal name for *invoking a stored query on a
|
|
named server* — it carries no query content (the stored query in the
|
|
catalog is the team's contract; the alias, its defaults, and its name are
|
|
yours):
|
|
|
|
```yaml
|
|
aliases:
|
|
triage:
|
|
server: intel-dev # names an entry under servers:
|
|
graph: spike # optional (multi-graph servers)
|
|
query: weekly_triage # the STORED query's name — never a file
|
|
args: [since] # positional args -> params, in order
|
|
params: { limit: 20 } # fixed defaults; positionals/--params win
|
|
format: table
|
|
```
|
|
|
|
`omnigraph query --alias triage 2026-06-01` invokes
|
|
`POST <server>/graphs/spike/queries/weekly_triage` with the keyed
|
|
credential. A legacy `omnigraph.yaml` alias with the same name wins during
|
|
the deprecation window (with a warning).
|
|
|
|
A remote command whose URL prefix-matches an operator server's `url` (the
|
|
`gh` host model — no flags needed) resolves its token through:
|
|
|
|
| Order | Source |
|
|
|---|---|
|
|
| 1 | `OMNIGRAPH_TOKEN_<NAME>` env (`prod` → `OMNIGRAPH_TOKEN_PROD`) |
|
|
| 2 | `[<name>]` section in `~/.omnigraph/credentials` |
|
|
| 3 | the legacy chain unchanged (`bearer_token_env` → `OMNIGRAPH_BEARER_TOKEN` → `auth.env_file`) |
|
|
|
|
A token is only ever sent to the server it is keyed to: URLs matching no
|
|
operator server use the legacy chain alone.
|
|
|
|
## `omnigraph.yaml` schema (legacy combined file)
|
|
|
|
> **Deprecated.** Loading this file prints a per-key notice
|
|
> naming each present key's new home (suppress in CI with
|
|
> `OMNIGRAPH_SUPPRESS_YAML_DEPRECATION=1`); `omnigraph config migrate`
|
|
> produces the split. The file keeps working through the deprecation
|
|
> window. Migrated teams can set `OMNIGRAPH_NO_LEGACY_CONFIG=1` to turn
|
|
> any legacy-file load into a hard error (regression guard; the file's
|
|
> absence is always fine).
|
|
|
|
```yaml
|
|
project: { name }
|
|
graphs:
|
|
<name>:
|
|
uri: <local|s3://|http(s)://>
|
|
bearer_token_env: <ENV_NAME>
|
|
queries: # per-graph stored-query registry (server-role; multi-graph mode)
|
|
<query-name>: # key MUST equal the `query <name>` symbol inside the .gq
|
|
file: <path-to-.gq> # relative to this config's directory
|
|
mcp:
|
|
expose: true # default true: listed in the MCP catalog (GET /queries); set false to hide (still HTTP-callable)
|
|
tool_name: <name> # optional MCP tool-name override (defaults to <query-name>;
|
|
# must be unique across exposed queries)
|
|
server:
|
|
graph: <name>
|
|
bind: <ip:port>
|
|
cli:
|
|
graph: <name>
|
|
branch: <name>
|
|
output_format: json|jsonl|csv|kv|table
|
|
table_max_column_width: 80
|
|
table_cell_layout: truncate|wrap
|
|
query:
|
|
roots: [<dir>, …] # search path for .gq files
|
|
auth:
|
|
env_file: .env.omni
|
|
aliases:
|
|
<alias>:
|
|
# accepted values: `read` / `query` (read alias), `change` / `mutate`
|
|
# (write alias). `query` and `mutate` are recommended; `read` and
|
|
# `change` remain accepted forever for back-compat.
|
|
command: read|change|query|mutate
|
|
query: <path-to-.gq>
|
|
name: <query-name>
|
|
args: [<positional-name>, …]
|
|
graph: <name>
|
|
branch: <name>
|
|
format: <output-format>
|
|
queries: # top-level registry — applies only to a bare-URI (anonymous) graph; a graph served by name uses its `graphs.<id>.queries`. Mirrors top-level `policy`.
|
|
<query-name>: { file: <path-to-.gq> } # mcp.expose defaults to true
|
|
policy:
|
|
file: policy.yaml
|
|
```
|
|
|
|
## Cluster config preview
|
|
|
|
```bash
|
|
omnigraph cluster validate --config company-brain
|
|
omnigraph cluster plan --config company-brain --json
|
|
omnigraph cluster apply --config company-brain --json
|
|
omnigraph cluster approve graph.<id> --config company-brain --as <actor>
|
|
omnigraph cluster status --config company-brain --json
|
|
omnigraph cluster refresh --config company-brain --json
|
|
omnigraph cluster import --config company-brain --json
|
|
omnigraph cluster force-unlock <LOCK_ID> --config company-brain --json
|
|
```
|
|
|
|
`--config` is a directory containing `cluster.yaml`; it defaults to `.`.
|
|
Stage 3A accepts graphs, schemas, stored queries, and policy bundle file
|
|
references. `cluster plan` reads local JSON state from
|
|
`<config-dir>/__cluster/state.json`; a missing file means empty state. Plan,
|
|
apply, refresh, and import acquire `__cluster/lock.json` by default and release
|
|
it before returning. `cluster apply` executes only stored-query/policy catalog
|
|
writes (content-addressed under `__cluster/resources/`) and requires an
|
|
existing `state.json`; graph/schema changes are deferred with warnings, and
|
|
applied resources do not serve traffic — the server still boots from
|
|
`omnigraph.yaml`. `cluster status` reads state only and reports any existing
|
|
lock metadata. `force-unlock` removes a lock only when the supplied id exactly
|
|
matches the lock file. `refresh` requires an existing `state.json`; `import`
|
|
creates one only when it is missing. Both observe declared graphs read-only at
|
|
`<config-dir>/graphs/<graph-id>.omni`. External state backends, graph/schema
|
|
apply, automatic stale-lock breaking, `plan --refresh`, pipelines, UI specs,
|
|
embeddings, aliases, and bindings are reserved for later stages. See
|
|
[cluster-config.md](../clusters/config.md).
|
|
|
|
## Output formats (`query` command, alias: `read`)
|
|
|
|
- `json` — pretty-printed object with metadata + rows
|
|
- `jsonl` — one metadata line then one JSON object per row
|
|
- `csv` — RFC 4180-ish quoting
|
|
- `table` — fitted text table, honors `table_max_column_width` + `table_cell_layout`
|
|
- `kv` — grouped per-row key/value blocks
|
|
|
|
## Param resolution
|
|
|
|
Precedence (high to low): explicit `--params` / `--params-file`, alias positional args, `omnigraph.yaml` defaults. JS-safe-integer handling is built in (`is_js_safe_integer_i64`, `JS_MAX_SAFE_INTEGER_U64`) so 64-bit ids round-trip safely through JSON clients.
|
|
|
|
## Bearer token resolution (CLI)
|
|
|
|
1. `graphs.<name>.bearer_token_env`
|
|
2. `OMNIGRAPH_BEARER_TOKEN` global env
|
|
3. `auth.env_file` referenced `.env`
|
|
|
|
## Duration parsing (cleanup)
|
|
|
|
`s | m | h | d | w` units, e.g. `--older-than 7d`.
|