Documentation
¶
Overview ¶
Package flows declares the profile, flow and image operations of the desktop's app-control MCP server.
Index ¶
Constants ¶
const DetailEmitted = "emitted"
DetailEmitted is flow_preview's own middle rung between DetailSummary and DetailFull: each node's emitted messages but not its received ones. A node's input is its upstream's output, and the entry node's is what the caller just sent, so received is derivable everywhere.
Variables ¶
var FlowGet = ops.Spec[ProfileIn]{ Name: "flow.get", Owner: ops.OwnerDesktop, Effect: ops.EffectRead, Toolset: Toolset, Summary: "Read a profile's flow", Guidance: "Return one profile's graph: every node with its id, type and config, and the wires between them. " + "This is where node ids come from — flow_preview's nodeId and the node-image tools take them. " + "A profile whose flow file does not parse still answers, with valid=false and the load error.", Args: []string{"profileId"}, }
var FlowPreview = ops.Spec[FlowPreviewIn]{ Name: "flow.preview", Owner: ops.OwnerDesktop, Effect: ops.EffectRead, Toolset: Toolset, Summary: "Dry-run a flow", Guidance: "Dry-run a flow against input you supply and report what every node did, committing nothing — no feed membership, " + "inbox rows, notifications, queued actions or durable kv. Name the flow with exactly one of profileId (an installed profile's flow, enabled or not), " + "flow (a flow document as a JSON object, same schema as flows/<id>.yaml, version included) or flowYaml (that document as YAML text), " + "so an unsaved edit can be executed before it is deployed. Messages are delivered to nodeId — any node, not only a source, which is how " + "one function node is exercised in isolation against a captured payload; flow_get is where node ids come from. " + "Sources never fetch: a source node relays what you inject. " + "Envelope fields left empty are filled in. kv seeds an in-memory sandbox (nodeId -> key -> value) that is the whole world a kv.get sees, " + "so notify-once logic is testable against a known starting state; what the run would have written comes back in kvMutations. " + "Each node reports its drops, timing, console output, and a structured error with line and column for a script failure, whose line " + "numbers are relative to the on_message body you wrote. detail controls how many copies of each message body come back: \"emitted\" " + "(the default) reports what each node put on its output ports, \"summary\" drops message bodies entirely — use it for a snapshot run, " + "whose payloads would otherwise be repeated once per node per item — and \"full\" adds each node's received messages. " + "Prefer this over deploying an edit and waiting for a poll.", Fields: ops.Constraints{"detail": {Enum: []string{ops.DetailSummary, DetailEmitted, ops.DetailFull}, Default: DetailEmitted}}, }
var NodeImageDelete = ops.Spec[NodeImageIn]{ Name: "node_image.delete", Owner: ops.OwnerDesktop, Effect: ops.EffectWrite, Toolset: Toolset, Summary: "Clear a source node's feed mark", Guidance: "Clear a source node's feed-mark image so its items revert to the node's icon.", Args: []string{"profileId", "nodeId"}, }
var NodeImageGet = ops.Spec[NodeImageIn]{ Name: "node_image.get", Owner: ops.OwnerDesktop, Effect: ops.EffectRead, Toolset: Toolset, Summary: "Read a source node's feed mark", Guidance: "Return a source node's feed-mark image as a PNG. A node with no mark is not_found — its items fall back to the node's glyph.", Args: []string{"profileId", "nodeId"}, }
var NodeImagePut = ops.Spec[NodeImagePutIn]{ Name: "node_image.put", Owner: ops.OwnerDesktop, Effect: ops.EffectWrite, Toolset: Toolset, Summary: "Set a source node's feed mark", Guidance: "Set a source node's feed-mark image from base64-encoded image bytes (PNG, JPEG, GIF or WebP). " + "It is normalized to a 128x128 PNG and shown on that source's items instead of its icon. " + "Webhook and command sources carry a mark; other node types are rejected.", Args: []string{"profileId", "nodeId"}, }
var ProfileCreate = ops.Spec[ProfileCreateIn]{ Name: "profile.create", Owner: ops.OwnerDesktop, Effect: ops.EffectWrite, Toolset: Toolset, Summary: "Create a profile", Guidance: "Create a profile, seeded with the starter graph when exactly one GitHub account is connected and empty otherwise.", Args: []string{"name"}, }
var ProfileDelete = ops.Spec[ProfileIn]{ Name: "profile.delete", Owner: ops.OwnerDesktop, Effect: ops.EffectWrite, Toolset: Toolset, Summary: "Delete a profile", Guidance: "Delete a profile, its flow files, its avatar, and its inbox state. This is not reversible. " + "An id that matches no profile is not_found — nothing is ever reported as deleted that was not.", Args: []string{"profileId"}, }
var ProfileImageDelete = ops.Spec[ProfileIn]{ Name: "profile_image.delete", Owner: ops.OwnerDesktop, Effect: ops.EffectWrite, Toolset: Toolset, Summary: "Clear a profile's avatar", Guidance: "Clear a profile's avatar so its rail reverts to the letter chip.", Args: []string{"profileId"}, }
var ProfileImageGet = ops.Spec[ProfileIn]{ Name: "profile_image.get", Owner: ops.OwnerDesktop, Effect: ops.EffectRead, Toolset: Toolset, Summary: "Read a profile's avatar", Guidance: "Return a profile's avatar as a PNG image. A profile with no avatar is not_found — its rail falls back to a letter chip.", Args: []string{"profileId"}, }
var ProfileImagePut = ops.Spec[ProfileImagePutIn]{ Name: "profile_image.put", Owner: ops.OwnerDesktop, Effect: ops.EffectWrite, Toolset: Toolset, Summary: "Set a profile's avatar", Guidance: "Set a profile's avatar from base64-encoded image bytes (PNG, JPEG, GIF or WebP — the format is sniffed, so no media type is needed). " + "The image is normalized to a 128x128 PNG.", Args: []string{"profileId"}, }
var ProfileList = ops.Spec[ops.NoInput]{ Name: "profile.list", Owner: ops.OwnerDesktop, Effect: ops.EffectRead, Toolset: Toolset, Summary: "List profiles", Guidance: "List every profile with its load status and whether it has an avatar. " + "Profile ids from here are what every other tool's profileId argument takes; flow_get turns one into its graph and node ids. " + "A profile whose flow file does not parse still lists, with valid=false.", }
var Toolset = ops.Toolset{Name: "flows", MCPPath: "/mcp"}
Functions ¶
Types ¶
type FlowPreviewIn ¶
type FlowPreviewIn struct {
ProfileID string `json:"profileId,omitempty" jsonschema:"An installed profile's id, enabled or not."`
// Flow is parsed through the same decoder a flows/<id>.yaml file goes
// through, so it carries the file's version too.
Flow map[string]any `json:"flow,omitempty" jsonschema:"A flow document as a JSON object, same schema as flows/<id>.yaml including version."`
FlowYAML string `json:"flowYaml,omitempty" jsonschema:"That same flow document as YAML text."`
// ProfileIDOverride only scopes the run's node KV and the feed ids in its
// outputs; nothing is installed under it.
ProfileIDOverride string `` /* 156-byte string literal not displayed */
NodeID string `json:"nodeId" jsonschema:"The node messages are delivered to — any node, not only a source. Node ids come from flow_get."`
Messages []Message `json:"messages" jsonschema:"The messages to deliver."`
// KV seeds the sandbox. Durable KV is neither read nor written, so this
// is the whole world a kv.get sees.
KV map[string]map[string]any `` /* 133-byte string literal not displayed */
Detail string `` /* 127-byte string literal not displayed */
}
FlowPreviewIn is one dry run. Exactly one of profileId, flow and flowYaml says which flow to run; the rest is the input and the sandbox it runs against.
func (FlowPreviewIn) Validate ¶
func (in FlowPreviewIn) Validate() error
type Message ¶
type Message struct {
ID string `json:"ID,omitempty" jsonschema:"Message id; a synthetic one is generated when empty."`
Key string `json:"Key,omitempty" jsonschema:"The source's own key for this item."`
Topic string `json:"Topic,omitempty" jsonschema:"Event topic; defaults to the source node's own topic when injecting at a source."`
Ts int64 `json:"Ts,omitempty" jsonschema:"Unix millisecond timestamp."`
Payload any `json:"Payload,omitempty" jsonschema:"The item's payload as arbitrary JSON."`
Snapshot []SnapshotItem `` /* 202-byte string literal not displayed */
SourceKind string `json:"SourceKind,omitempty" jsonschema:"The source connector kind."`
SourceScope string `json:"SourceScope,omitempty" jsonschema:"The source's scope within that kind."`
OccurrenceKey string `json:"OccurrenceKey,omitempty" jsonschema:"Distinguishes repeat occurrences of one item."`
}
Message mirrors the desktop's message envelope for the wire. That envelope carries its raw JSON as json.RawMessage, whose Go type is []byte, so the schema inferrer would describe Payload as an array and reject the JSON object every real payload is (ADR mcp-replaces-the-agent-facing-http-api). any is what the schema has to say for arbitrary JSON.
Field names match the envelope's own JSON (Go names, not lowercased) so a payload captured from the app round-trips unchanged.
type NodeImageIn ¶
type NodeImageIn struct {
ProfileID string `json:"profileId" jsonschema:"Profile id from profile_list."`
NodeID string `json:"nodeId" jsonschema:"Node id within that profile's flow; ids come from flow_get."`
}
NodeImageIn names one node of a profile's flow.
type NodeImagePutIn ¶
type NodeImagePutIn struct {
ProfileID string `json:"profileId" jsonschema:"Profile id from profile_list."`
NodeID string `json:"nodeId" jsonschema:"Node id within that profile's flow; ids come from flow_get."`
ImageBase64 string `json:"imageBase64" jsonschema:"The image as base64 (PNG, JPEG, GIF or WebP). A data: URL is accepted and its prefix ignored."`
}
type ProfileCreateIn ¶
type ProfileCreateIn struct {
Name string `json:"name" jsonschema:"Display name for the new profile."`
}
type ProfileImagePutIn ¶
type ProfileIn ¶
type ProfileIn struct {
ProfileID string `json:"profileId" jsonschema:"Profile id from profile_list."`
}
ProfileIn names one profile.