cmdutil

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package cmdutil provides the Factory injected into every command: lazily-resolved config, token store, and API client, plus the values of the persistent --json/--server/--app flags.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BatchNodeRef added in v0.9.0

func BatchNodeRef(memory, ref string) (string, error)

BatchNodeRef canonicalizes a node reference for a server op that resolves a PK-or-URN itself, WITHOUT the resolveUrn round trip ResolveNodeRef costs — today nodeBatch(refs:), which accepts both since hadron-server#813. It takes the same inputs ResolveNodeRef does (a fully-qualified URN, or a bare loc with -m/--memory) and applies the same composition, but returns the ref to send instead of an id. That is what makes a batch of URNs ONE call rather than N resolves plus a batch.

It rejects locally what the server would reject loudly: the server errors on a malformed / unqualified ref rather than listing it as unavailable, and one mistyped ref must not cost the other nineteen their read. Callers map the returned Usage error onto their own unavailable list.

func CanonicalMemoryRef added in v0.6.0

func CanonicalMemoryRef(ref string) string

CanonicalMemoryRef normalizes a memory reference to the canonical grammar-v2 flat URN hrn:mem:<root>:<slug> (#697 emission flip) for the server's memory(ref:) dispatch. A raw id (no separator) or an unrecognized shape passes through untouched — the server accepts every legacy spelling forever (#239), so the short forms the CLI advertises resolve consistently either way (#108).

func CanonicalNodeRef added in v0.8.0

func CanonicalNodeRef(ref string) string

CanonicalNodeRef canonicalizes a node reference for a server op that itself accepts an ID or a URN (spec 007 dispatch) — e.g. the object store's object(ref:)/updateObject/deleteObject, which forward ref to node(ref:). A scheme-prefixed URN passes through, a bare/legacy fully-qualified node URN (<org>::<memory>::<loc>) gets the canonical hrn:node: prefix, and a raw id (or any unrecognized shape) is left for the server to resolve. Unlike ResolveNodeURN it does NOT round-trip through resolveUrn, so a raw object id works without a lookup and without being rejected as "not a URN".

func CanonicalNodeURN added in v0.6.0

func CanonicalNodeURN(ref string) (string, error)

CanonicalNodeURN validates and normalizes a fully-qualified entry-node URN for the headless-run surface (schedule/webhook/trigger entryNodeUrn). It is the no-network half of ResolveNodeURN: a scheme-prefixed ref (hrn:/urn:) passes through verbatim; a bare <org>::<memory>::<loc> gets the canonical hrn:node: prefix; anything without the two `::` separators is rejected as ambiguous. The result is a URN the server stores, not a node ID — the entry node is resolved at run time, not now.

func CanonicalizeURN added in v0.8.0

func CanonicalizeURN(flag, input string) (string, error)

CanonicalizeURN validates a scheme-prefixed Hadron URN and returns its parser-canonical form. It is intentionally kept as a thin wrapper for the spec-047 golden tests, which pin CLI parser parity to urn-lib-go.

func Confirm added in v0.2.0

func Confirm(io *output.IOStreams, yes bool, prompt string) error

Confirm gates a significant but non-deletion action behind the same rule as ConfirmDeletion: --yes skips the prompt, an interactive terminal is asked y/N, and a non-interactive caller without --yes is refused with a Usage error so the action is always explicit. `prompt` is the full question, without the trailing " (y/N)".

func ConfirmDeletion

func ConfirmDeletion(io *output.IOStreams, yes bool, what string) error

ConfirmDeletion gates destructive commands. --yes skips the prompt; otherwise an interactive terminal asks y/N, and a non-interactive caller (scripts, agents) is refused with a Usage error so deletions are always explicit.

func DescribeUser added in v0.9.0

func DescribeUser(u gen.UserFields) string

DescribeUser renders a user for a confirmation prompt or an error: the id always, plus whichever human identifiers exist. The id alone is unreadable and the handle alone is not unique enough to confirm against.

func EdgeDisplay added in v0.3.0

func EdgeDisplay(name *string, loc string) string

EdgeDisplay is the human handle for an edge: its name, or its loc when the name is empty (spec 037 — an edge's name is optional, its loc is the identity, so a nameless edge still prints something addressable).

func IsNodeID added in v0.9.0

func IsNodeID(ref string) bool

IsNodeID reports whether ref is a bare node id rather than a URN or loc.

Every --json surface prints these (`id`, and `otherNodeId` on each edge) and both node(ref:) and nodeBatch(refs:) accept them, so a ref the CLI just emitted has to be feedable straight back (#336). It is matched by SHAPE, not merely by "contains no colon": a bare loc typed without -m (`start-here`) is also colon-free, and treating that as an id would swap a usage error naming -m for a bare "not found".

Note resolveUrn does NOT accept an id — it returns null for one — so callers short-circuit rather than round-tripping.

NOT widened to the CUID the schema also names as a PK form ("PK (CUID / 32-char hex)"). A CUID-shaped rule — letter-led lowercase alphanumeric — is indistinguishable from an ordinary loc, and would capture 16 real ones in the sampled memories, including `preflight`, `instructions`, `conventions` and `findings`: `node get preflight` would stop reporting "pass -m" and start reporting "not found". Nothing is lost by the narrow gate — a CUID-backed node is still addressable by its URN — whereas widening breaks refs that work today.

func IsQualifiedNodeRef added in v0.9.0

func IsQualifiedNodeRef(ref string) bool

IsQualifiedNodeRef reports whether ref already names its own memory — a scheme-prefixed URN (hrn:node:/urn:node:) or the legacy fully-qualified <org>::<memory>::<loc>. It is the same grammar ResolveNodeURN accepts, in predicate form.

It exists for commands whose -m/--memory is REQUIRED for another reason (it names where a write lands, say) but which also take a node ref that may point at another memory. ResolveNodeRef treats its ref as a bare loc whenever a memory is supplied, so passing a qualified ref through composes it INTO that memory — `hrn:node:acme.com:kb:hrn:node:other.org:dev:x`, which resolves to nothing, or "" for the legacy spelling. Gate on this and pass an empty memory for qualified refs so they resolve where they actually live.

A bare node id is NOT covered here and needs no gate: ResolveNodeRef short-circuits on IsNodeID before -m is considered.

func KeyValsToJSON added in v0.6.0

func KeyValsToJSON(pairs []string, flag string) (*json.RawMessage, error)

KeyValsToJSON assembles repeated key=value flags into a JSON object (e.g. `--arg k=v` → an eventData object, `--param k=v` → a provider-params object). Each value is sent as JSON when it parses as JSON (numbers, booleans, arrays, objects), otherwise as a string. Returns nil for no pairs so the variable is omitted, not sent as `{}`. `flag` names the source flag in the error.

func MemoryParts added in v0.8.0

func MemoryParts(ref string) (root, slug string, ok bool)

MemoryParts decomposes a memory reference into its grammar-v2 (root, slug) atoms. It accepts every spelling the CLI advertises — a bare "org:slug" or "org::slug", or an hrn:memory:/urn:memory: (v1) / hrn:mem:/urn:mem: (v2) URN — and normalizes the separator either way. ok is false for a raw id (no separator), an unrelated scheme (hrn:node:…), or a malformed multi-part ref; callers pass those through for the server to resolve.

func NodeURN added in v0.6.0

func NodeURN(memory, loc string) string

NodeURN composes the canonical grammar-v2 flat node URN hrn:node:<root>:<slug>:<loc…> for a (memory, loc). The memory's separators are normalized (single-colon and legacy "::" both accepted). It returns "" when the memory isn't an <org>::<slug> pair (a raw id, or a malformed multi-part ref) or loc is empty — composing a URN from those would produce an invalid one that resolves to nothing, silently defeating the existence probe (#129 review). The caller must probe otherwise.

func ParseJSONArg added in v0.6.0

func ParseJSONArg(s, what string) (*json.RawMessage, error)

ParseJSONArg parses a JSON-document flag (--policy, --args-schema). An empty string means "not provided" and returns nil so the variable is omitted; a non-empty value must be valid JSON. The `what` label names the flag in the error.

func ParseNodePropertySort added in v0.8.0

func ParseNodePropertySort(raw string) (*gqltypes.NodePropertySort, error)

ParseNodePropertySort parses the raw-JSON `--sort-property` value (server NodePropertySort, #719) into the bound gqltypes struct. Same grammar-parity contract as ParseNodeWhere: keys are path (required), field, as, direction.

func ParseNodeWhere added in v0.8.0

func ParseNodeWhere(raw string) (*gqltypes.NodeWhereInput, error)

ParseNodeWhere parses the raw-JSON `--where` predicate (grammar parity with the server's NodeWhereInput, #719) into the bound gqltypes struct. The JSON keys are the GraphQL field names verbatim (and/or/not, path, field, as, and one of eq|ne|in|lt|lte|gt|gte|between|exists|contains), so a user's predicate unmarshals straight through — the struct's omitempty tags then omit every field they left unset, which the server's "exactly one operator" leaf check requires (it counts any operator key that is not undefined). Deep validation (depth, leaf-arity, path shape) is the server's job and surfaces as BAD_USER_INPUT; this only enforces well-formed JSON and rejects unknown keys so a typo like "equals" fails loudly instead of being silently dropped.

func ResolveAppRef added in v0.6.0

func ResolveAppRef(f *Factory, flag string) (string, error)

ResolveAppRef resolves the App a headless-run command targets: the explicit --app flag when set, otherwise the configured/global App context (the same source `hadron app use` and the persistent --app write). Empty both ways is a usage error — every run/schedule/webhook command names an App. The ref is passed to the server verbatim, which dispatches an ID or a URN.

func ResolveNodeRef added in v0.3.0

func ResolveNodeRef(cmd *cobra.Command, client graphql.Client, memory, ref string) (string, error)

ResolveNodeRef resolves a node reference into a node ID. With an empty memory it requires a fully-qualified URN (ResolveNodeURN). With a memory (the `org::memory` form, optionally hrn:/urn:-prefixed) the ref is a bare loc within that memory: a node URN is just <org>::<memory>::<loc>, so the two are joined and resolved. The memory form is the additive convenience; without it the strict-URN behavior is unchanged.

func ResolveNodeURN

func ResolveNodeURN(cmd *cobra.Command, client graphql.Client, ref string) (string, error)

ResolveNodeURN turns a fully-qualified node URN into a node ID via Query.resolveUrn. Bare locs are rejected client-side with a usage error: node references always name the memory (same-loc collisions across memories made anything less ambiguous). A URN that resolves to a different entity kind is a usage error too.

func ResolveTextInput added in v0.3.0

func ResolveTextInput(flag, value, file string, stdin io.Reader) (string, error)

ResolveTextInput resolves a text field that may be supplied inline, from a file, or on stdin — the same convention the --content / --content-file / "-" flags already use, factored out so every write flag behaves identically.

flag is the user-facing flag name (e.g. "abstract"): the inline value comes from --<flag>, the file from --<flag>-file, and a sentinel value of "-" reads stdin. The inline value and the file are mutually exclusive. Callers that gate on cmd.Flags().Changed decide whether an empty result means "clear" (an explicit empty string) or "leave unset".

A paragraph-length field with backticks or newlines is hostile to inline shell quoting, so --<flag>-file / stdin are the ergonomic path (issue #38).

func ResolveUser added in v0.9.0

func ResolveUser(cmd *cobra.Command, client graphql.Client, ref string) (fields gen.UserFields, found bool, err error)

ResolveUser is ResolveUserID's underlying lookup, returning everything the resolution already read instead of just the id — both paths fetch the full UserFields, so a caller that also needs the user's current state (roles, handle) gets it without a second round trip.

found reports whether a user was actually matched. It is false only for the pass-through case described above, where an unmatched bare token is returned verbatim as a literal id and fields carries nothing but that Id — the caller decides whether that is enough to act on.

func ResolveUserExactly added in v0.9.0

func ResolveUserExactly(cmd *cobra.Command, client graphql.Client, ref string) (fields gen.UserFields, found bool, err error)

ResolveUserExactly is ResolveUser restricted to an EXACT identifier match.

ResolveUser deliberately falls back to a sole substring hit, which is right for additive operations (sharing a memory with "alic" when only "alice" matches is a convenience). It is wrong for a destructive global write: a typo would silently retarget a different account. Commands that overwrite or remove state should resolve through this instead, so a partial match is a usage error naming what it matched rather than a silent retarget.

The pass-through case is preserved: a bare token nothing matched is still returned verbatim as a literal id with found=false, so an explicit id the caller cannot read still works.

func ResolveUserID added in v0.9.0

func ResolveUserID(cmd *cobra.Command, client graphql.Client, ref string) (string, error)

ResolveUserID turns a user reference (id, email, handle, or an hrn:user:/urn:user: URN wrapping one of those) into a User ID — the form the server's user-keyed mutations accept today (there is no user-URN support yet, hadron-server#325). An id- or handle-shaped ref resolves via the uniform user(ref:) find-one first (one round trip, no paging); anything else — or a find-one miss — goes through users(filter: { query }), which is itself access-scoped to the caller. A bare token neither path can match is passed through verbatim as a literal id so an explicit id always works; an email/handle that matches nothing is a not-found error.

Shared by `access check` (resource authorization) and `memory share` (grantee), so both accept the same ref forms (hadron-cli#280).

func ValidateAgentURNPath added in v0.8.0

func ValidateAgentURNPath(flag, path string) error

ValidateAgentURNPath checks an agent slug path supplied as a partial URN component. Unlike a node loc, an agent path may include owner/author context; urn-lib-go owns those shape rules.

func ValidateOrgSlug added in v0.9.0

func ValidateOrgSlug(flag, slug string) error

ValidateOrgSlug checks an organization slug supplied on a create/rename flag. An org root must be a bare, dotted domain (no scheme prefix or colon) that also satisfies the shared slug rules — the shared urn-lib owns the policy (grammar-v2 / #692), so the CLI fails fast with the same rule the server enforces rather than surfacing a less actionable server error.

func ValidateURNPath added in v0.7.0

func ValidateURNPath(flag, path string) error

ValidateURNPath checks a colon-delimited node loc. A loc is a single hierarchy leaf whose atoms are separated by single colons.

func ValidateURNSlug added in v0.7.0

func ValidateURNSlug(flag, slug string) error

ValidateURNSlug checks a single URN slug atom supplied on a create/rename flag. The shared urn-lib owns the create-time slug policy.

func ValidateUserHandle added in v0.9.0

func ValidateUserHandle(flag, handle string) error

ValidateUserHandle checks a user handle supplied on a create/rename flag. A handle is a slug that must additionally be dot-free, so it stays disjoint from dotted org roots in the shared principal pool (#692). The shared urn-lib owns the policy.

Types

type Factory

type Factory struct {
	IOStreams  *output.IOStreams
	HTTPClient *http.Client

	// Persistent flag values, bound by the root command.
	JSON       bool
	ServerFlag string
	AppFlag    string

	// Overridable for tests.
	ConfigFn     func() (*config.Config, error)
	TokenStoreFn func() store.Store
	// contains filtered or unexported fields
}

func NewFactory

func NewFactory() *Factory

func (*Factory) App

func (f *Factory) App() (string, error)

App resolves the App URN context: --app flag, then config default. Empty means no App context, which the server treats as fine.

func (*Factory) Config

func (f *Factory) Config() (*config.Config, error)

func (*Factory) GraphQLClient

func (f *Factory) GraphQLClient() (graphql.Client, error)

GraphQLClient returns an authenticated genqlient client, failing with the AuthRequired exit code when no credentials are present.

func (*Factory) NoteAppIsContextOnly added in v0.9.0

func (f *Factory) NoteAppIsContextOnly(scope string)

NoteAppIsContextOnly warns on stderr that an explicitly-passed --app did NOT scope this listing (#383). `--app` is the persistent App-CONTEXT flag, not a filter, so `agent list --app <A>` returns the same rows for every <A> — which is how an agent installed only in another org's App came to be printed as though it were on this team.

Only the explicit flag triggers the note: a configured default App is ambient context nobody passed expecting a filter, so noting it on every invocation would be pure noise. Stderr keeps the --json stdout contract untouched.

func (*Factory) PublicGraphQLClient added in v0.9.0

func (f *Factory) PublicGraphQLClient() (client graphql.Client, authenticated bool, err error)

PublicGraphQLClient returns a client for the server's PUBLIC surface. It attaches credentials when they exist but does NOT require them, so a query the server serves anonymously still works when signed out.

authenticated reports whether credentials were attached — a caller can then say which surface the answer came from instead of implying it was privileged. A corrupt token store still errors rather than silently degrading to anonymous (#125): failing loud beats a confusing half-answer.

func (*Factory) Server

func (f *Factory) Server() (string, error)

Server resolves the server base URL: --server flag, then HADRON_SERVER env, then config, then the hosted default.

func (*Factory) Token

func (f *Factory) Token() (string, auth.TokenSource, error)

Token returns the active token and its source for the resolved server ("" source when unauthenticated). HADRON_TOKEN is checked before the token store so CI never triggers a keyring probe.

func (*Factory) TokenStore

func (f *Factory) TokenStore() store.Store

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL