Skip to content

About

Herdr socket api for golang

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

herdr-sock-go

Go client for Herdr's local socket API.

This package speaks Herdr's newline-delimited JSON API over the local API socket. The typed API targets the Herdr 0.9.0 source at commit 61ca85d5895bb530b59da85beeccdd767f4720d2, which reports internal protocol 22. The separate binary pane.graphics.stream protocol, including file-frame transport, is not part of this package.

Herdr 0.9.0 Update

go get github.com/lib-x/herdr-sock-go@v0.9.0

This release adds typed helpers for pane scrolling, selection reads, copy motions and searches, right-click routing, link activation, workspace block moves, group closure, integration discovery, command invocation, and dismissal of announcements and release notes. Existing helper signatures are retained.

  • WorkspaceCreateParams.SourceWorkspaceID selects the workspace used by the follow working-directory policy.
  • Worktree list/create/open/remove parameters now accept TrustRepository. It defaults to false; set it explicitly when you intend to trust repository lifecycle hooks.
  • PaneSplitParams.RightClick and SetPaneInput select herdr or pane right-click routing.
  • PaneGraphicsSetParams accepts LayerID, ZIndex, and BGRA images. ClearPaneGraphicsLayer clears a named layer; ClearPaneGraphics still clears the default primary layer. GetPaneGraphicsInfo now returns visibility, layer limits, pixel-mouse support, and file-frame capabilities.
  • SubscribeWorkspaceReordered subscribes to workspace.reordered; decode its workspace_reordered payload with WorkspaceReorderedEvent.

Use Ping to inspect ServerCapabilities, including endpoint generation, surface interest, and health-check support. RequireCurrentProtocol remains an optional strict check for protocol 22; it does not negotiate JSON API compatibility. The stable client endpoint generation is a separate contract. client_shell.surface.set has a method constant and wire types for schema reference, but requires a client-shell endpoint and returns connection_local_only over this package's local JSON socket transport.

Selection and link requests can include ContentRevision to reject stale coordinates; copy search requires it. For example:

result, err := client.SearchPaneCopy(ctx, herdr.PaneCopySearchParams{
    PaneID:          "w1:p1",
    Query:           "error",
    Direction:       herdr.PaneCopySearchDirectionForward,
    Cursor:          herdr.PaneTextPoint{Row: 0, Col: 0},
    ContentRevision: contentRevision, // Revision of the selected terminal content.
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(result.Total, result.Current)

InvokeCommand accepts an opaque CommandID issued by Herdr's client-shell projection, not shell command text. New methods require a server that supports them; older servers may reject them even when existing JSON methods still work.

Herdr 0.7.5 Migration

Herdr 0.7.5 uses protocol 17 and is not wire-compatible with the protocol 16 API targeted by this module's v0.7.4 tag. This release also contains Go source-level changes that mirror Herdr's breaking Agent API update:

  • MethodAgentSend, AgentSendParams, and Client.SendAgent were removed. Use SendAgentKeys for logical key input or PromptAgent to submit text.
  • AgentStartParams now requires Name, Kind, and PaneID; use Args instead of the previous topology, environment, and Argv fields.
  • StartAgent submits into an existing pane and can return before the agent is interactive. Check InteractiveReady as described below.
package main

import (
	"context"
	"fmt"
	"log"

	herdr "github.com/lib-x/herdr-sock-go"
)

func main() {
	ctx := context.Background()
	client := herdr.MustNew()

	if _, err := client.RequireCurrentProtocol(ctx); err != nil {
		log.Fatal(err)
	}

	pane, err := client.CurrentPane(ctx, nil)
	if err != nil {
		log.Fatal(err)
	}

	stripANSI := true
	read, err := client.ReadPane(ctx, herdr.PaneReadParams{
		PaneID:    pane.PaneID,
		Source:    herdr.ReadRecent,
		Format:    herdr.ReadFormatText,
		StripANSI: &stripANSI,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(read.Text)
}

Socket Resolution

New() follows Herdr's API socket resolution:

  1. WithSocketPath(path)
  2. WithSession(name)
  3. HERDR_SOCKET_PATH
  4. HERDR_SESSION
  5. the default socket under the Herdr config directory

Use Call or CallRaw for methods that do not yet have a typed helper:

var result map[string]any
err := client.Call(ctx, "", herdr.MethodPaneProcessInfo, map[string]any{
	"pane_id": "w1:p1",
}, &result)

Metadata

Pane and workspace metadata token patches use map[string]*string. A non-nil pointer sets the value and a nil pointer clears the token:

summary := "reviewing authentication"
ttl := uint64(60_000)
err := client.ReportWorkspaceMetadata(ctx, herdr.WorkspaceReportMetadataParams{
	WorkspaceID: "w1",
	Source:      "my-tool",
	Tokens: map[string]*string{
		"summary": &summary,
		"old":     nil,
	},
	TTLMS: &ttl,
})

WorkspaceInfo, PaneInfo, and AgentInfo expose resolved token maps. PaneInfo and AgentInfo also expose TerminalTitle and TerminalTitleStripped.

Events

status := herdr.AgentStatusDone
stream, err := client.Subscribe(ctx,
	herdr.SubscribePaneAgentStatusChanged("w1:p1", &status),
)
if err != nil {
	log.Fatal(err)
}
defer stream.Close()

event, err := stream.Next(ctx)
if err != nil {
	log.Fatal(err)
}
fmt.Println(event.Event)

Metadata snapshot events are available through subscription helpers:

stream, err := client.Subscribe(ctx,
	herdr.SubscribeWorkspaceMetadataUpdated(),
	herdr.SubscribePaneUpdated(),
)

Subscription filters use dot names. Pushed lifecycle envelopes use Herdr's snake-case event kinds, such as pane_agent_detected; the specialized output, agent-status, and scroll subscriptions retain their documented dot names.

Agent Workflows

Herdr 0.7.5 replaces agent.send with logical key input and adds atomic prompt and wait methods. Agent startup now targets an existing pane:

timeout := uint64(20_000)
agent, argv, err := client.StartAgent(ctx, herdr.AgentStartParams{
	Name:      "reviewer",
	Kind:      "codex",
	PaneID:    "w1:p1",
	Args:      []string{"--full-auto"},
	TimeoutMS: &timeout,
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(agent.PaneID, argv)

StartAgent returns after submitting the launch command. The returned agent may still have LaunchPending=true and InteractiveReady=false; check it with GetAgent before prompting. WaitAgent waits for semantic status, not startup readiness. TimeoutMS controls how long Herdr permits the managed launch to become ready and must be between 3001 and 300000 milliseconds.

Submit a prompt and wait for the same live agent to settle:

prompted, err := client.PromptAgent(ctx, herdr.AgentPromptParams{
	Target: "reviewer",
	Text:   "Review the current diff",
	Wait: &herdr.AgentPromptWaitOptions{
		Until:     []herdr.AgentStatus{herdr.AgentStatusDone, herdr.AgentStatusBlocked},
		TimeoutMS: &timeout,
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(prompted.AgentStatus)

Use SendAgentKeys for logical keys such as up and enter, or WaitAgent when no prompt needs to be submitted. WithTimeout is an outer request cap and the earlier client or context deadline wins. Use WithTimeout(0) to rely only on the context and Herdr's TimeoutMS; otherwise leave margin above the server timeout.

Herdr 0.7.5 also supports transient declarative Agent views:

source := "example.agent-view"
label := "attention"
view, err := client.SetAgentView(ctx, herdr.AgentViewSetParams{
	Source: source,
	Label:  &label,
	Filter: herdr.AgentViewIn(
		herdr.AgentViewFieldStatus,
		herdr.AgentStatusBlocked,
		herdr.AgentStatusDone,
	),
	Sort: []herdr.AgentViewSort{
		{Field: herdr.AgentViewSortAttention, Order: herdr.AgentViewSortDescending},
		{Field: herdr.AgentViewSortStateChangeSeq, Order: herdr.AgentViewSortDescending},
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(view.Active)

defer func() {
	_, _ = client.ClearAgentView(context.Background(), herdr.AgentViewClearParams{
		Source: &source,
	})
}()

The view remains active until it is cleared or replaced, its owning plugin is disabled or removed, or the Herdr server exits. Clearing with the same source avoids removing a view owned by a different source.

Popup Plugin Panes

Open a plugin entrypoint as a floating popup with cell or percentage sizing:

placement := herdr.PluginPanePopup
width, err := herdr.PopupPercent(80)
if err != nil {
	log.Fatal(err)
}
height := herdr.PopupCells(24)

pluginPane, err := client.OpenPluginPane(ctx, herdr.PluginPaneOpenParams{
	PluginID:   "pomodoro",
	Entrypoint: "main",
	Placement:  &placement,
	Width:      &width,
	Height:     &height,
	Focus:      true,
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(pluginPane.Pane.PaneID)

defer client.ClosePopup(ctx)

Herdr's ui.copy_on_select remains a native configuration setting; it is not a socket API method.

Examples

Run the current pane reader:

go run ./examples/current-pane

Watch agent status changes for a pane:

go run ./examples/watch-agent w1:p1

About

Herdr socket api for golang

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages