@stateforge-labs/herdr-sdk 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/NOTICE.md +51 -0
- package/README.md +211 -2
- package/bin/herdr-drift.js +14 -0
- package/bin/herdr-skill.js +38 -0
- package/dist/admin.d.ts +2 -0
- package/dist/admin.js +3 -0
- package/dist/constants.d.ts +236 -0
- package/dist/constants.js +294 -0
- package/dist/core/backoff.d.ts +20 -0
- package/dist/core/backoff.js +53 -0
- package/dist/core/constants.d.ts +143 -0
- package/dist/core/constants.js +98 -0
- package/dist/core/errors.d.ts +35 -0
- package/dist/core/errors.js +16 -0
- package/dist/core/events.d.ts +42 -0
- package/dist/core/events.js +128 -0
- package/dist/core/kernel.d.ts +65 -0
- package/dist/core/kernel.js +138 -0
- package/dist/core/link.d.ts +47 -0
- package/dist/core/link.js +187 -0
- package/dist/core/test-kit/constants.d.ts +51 -0
- package/dist/core/test-kit/constants.js +43 -0
- package/dist/core/test-kit/fake-core.d.ts +61 -0
- package/dist/core/test-kit/fake-core.js +228 -0
- package/dist/core/test-kit/frames.d.ts +1 -0
- package/dist/core/test-kit/frames.js +480 -0
- package/dist/core/test-kit/setup-test.d.ts +18 -0
- package/dist/core/test-kit/setup-test.js +35 -0
- package/dist/core/wire.d.ts +85 -0
- package/dist/core/wire.js +199 -0
- package/dist/core-test-kit.d.ts +5 -0
- package/dist/core-test-kit.js +5 -0
- package/dist/core.d.ts +15 -0
- package/dist/core.js +12 -0
- package/dist/drift/cli.d.ts +22 -0
- package/dist/drift/cli.js +146 -0
- package/dist/drift/constants.d.ts +83 -0
- package/dist/drift/constants.js +73 -0
- package/dist/drift/diff.d.ts +7 -0
- package/dist/drift/diff.js +91 -0
- package/dist/drift/schema.d.ts +19 -0
- package/dist/drift/schema.js +190 -0
- package/dist/drift/types.d.ts +76 -0
- package/dist/drift/types.js +1 -0
- package/dist/drift.d.ts +5 -0
- package/dist/drift.js +6 -0
- package/dist/effect.d.ts +3 -0
- package/dist/effect.js +4 -0
- package/dist/herdr/client/connect.d.ts +5 -0
- package/dist/herdr/client/connect.js +81 -0
- package/dist/herdr/client/held.d.ts +54 -0
- package/dist/herdr/client/held.js +100 -0
- package/dist/herdr/client/long-poll.d.ts +33 -0
- package/dist/herdr/client/long-poll.js +66 -0
- package/dist/herdr/client/scoped.d.ts +45 -0
- package/dist/herdr/client/scoped.js +241 -0
- package/dist/herdr/client/streams.d.ts +9 -0
- package/dist/herdr/client/streams.js +40 -0
- package/dist/herdr/client/subscribe.d.ts +75 -0
- package/dist/herdr/client/subscribe.js +208 -0
- package/dist/herdr/client/types.d.ts +150 -0
- package/dist/herdr/client/types.js +1 -0
- package/dist/herdr/client/watch-pane-set.d.ts +34 -0
- package/dist/herdr/client/watch-pane-set.js +271 -0
- package/dist/herdr/client/watch.d.ts +117 -0
- package/dist/herdr/client/watch.js +138 -0
- package/dist/herdr/effect/to-tagged-error.d.ts +2 -0
- package/dist/herdr/effect/to-tagged-error.js +54 -0
- package/dist/herdr/effect/types.d.ts +51 -0
- package/dist/herdr/effect/types.js +26 -0
- package/dist/herdr/errors.d.ts +53 -0
- package/dist/herdr/errors.js +87 -0
- package/dist/herdr/generated/entries/admin.d.ts +2 -0
- package/dist/herdr/generated/entries/admin.js +5 -0
- package/dist/herdr/generated/entries/client.d.ts +2 -0
- package/dist/herdr/generated/entries/client.js +5 -0
- package/dist/herdr/generated/entries/held.d.ts +2 -0
- package/dist/herdr/generated/entries/held.js +5 -0
- package/dist/herdr/generated/entries/prompt.d.ts +2 -0
- package/dist/herdr/generated/entries/prompt.js +5 -0
- package/dist/herdr/generated/entries/read-only-content.d.ts +2 -0
- package/dist/herdr/generated/entries/read-only-content.js +5 -0
- package/dist/herdr/generated/entries/read-only.d.ts +2 -0
- package/dist/herdr/generated/entries/read-only.js +5 -0
- package/dist/herdr/generated/entry-methods.d.ts +9 -0
- package/dist/herdr/generated/entry-methods.js +17 -0
- package/dist/herdr/generated/errors.d.ts +4 -0
- package/dist/herdr/generated/errors.js +3 -0
- package/dist/herdr/generated/events.d.ts +274 -0
- package/dist/herdr/generated/events.js +3 -0
- package/dist/herdr/generated/index.d.ts +20 -0
- package/dist/herdr/generated/index.js +19 -0
- package/dist/herdr/generated/method-table.d.ts +615 -0
- package/dist/herdr/generated/method-table.js +106 -0
- package/dist/herdr/generated/names/agent-status.d.ts +10 -0
- package/dist/herdr/generated/names/agent-status.js +10 -0
- package/dist/herdr/generated/names/capability-class.d.ts +14 -0
- package/dist/herdr/generated/names/capability-class.js +15 -0
- package/dist/herdr/generated/names/event-kind.d.ts +31 -0
- package/dist/herdr/generated/names/event-kind.js +31 -0
- package/dist/herdr/generated/names/event-match-kind.d.ts +23 -0
- package/dist/herdr/generated/names/event-match-kind.js +24 -0
- package/dist/herdr/generated/names/index.d.ts +10 -0
- package/dist/herdr/generated/names/index.js +12 -0
- package/dist/herdr/generated/names/method.d.ts +209 -0
- package/dist/herdr/generated/names/method.js +209 -0
- package/dist/herdr/generated/names/result-tag.d.ts +68 -0
- package/dist/herdr/generated/names/result-tag.js +68 -0
- package/dist/herdr/generated/names/server-capability.d.ts +10 -0
- package/dist/herdr/generated/names/server-capability.js +11 -0
- package/dist/herdr/generated/names/subscription-event-kind.d.ts +8 -0
- package/dist/herdr/generated/names/subscription-event-kind.js +8 -0
- package/dist/herdr/generated/names/subscription-type.d.ts +31 -0
- package/dist/herdr/generated/names/subscription-type.js +32 -0
- package/dist/herdr/generated/names/token-name-pattern.d.ts +2 -0
- package/dist/herdr/generated/names/token-name-pattern.js +4 -0
- package/dist/herdr/generated/namespaces.d.ts +785 -0
- package/dist/herdr/generated/namespaces.js +3 -0
- package/dist/herdr/generated/overrides.d.ts +33 -0
- package/dist/herdr/generated/overrides.js +20 -0
- package/dist/herdr/generated/params.d.ts +799 -0
- package/dist/herdr/generated/params.js +3 -0
- package/dist/herdr/generated/preset-methods.d.ts +4 -0
- package/dist/herdr/generated/preset-methods.js +7 -0
- package/dist/herdr/generated/presets/sessions-reader.d.ts +2 -0
- package/dist/herdr/generated/presets/sessions-reader.js +5 -0
- package/dist/herdr/generated/results.d.ts +978 -0
- package/dist/herdr/generated/results.js +52 -0
- package/dist/herdr/generated/test-data/defaults.d.ts +739 -0
- package/dist/herdr/generated/test-data/defaults.js +741 -0
- package/dist/herdr/generated/test-data/shapes.d.ts +1325 -0
- package/dist/herdr/generated/test-data/shapes.js +2245 -0
- package/dist/herdr/test-kit/fake-herdr.d.ts +13 -0
- package/dist/herdr/test-kit/fake-herdr.js +216 -0
- package/dist/herdr/test-kit/fake-protocol.d.ts +107 -0
- package/dist/herdr/test-kit/fake-protocol.js +436 -0
- package/dist/herdr/test-kit/types.d.ts +88 -0
- package/dist/herdr/test-kit/types.js +1 -0
- package/dist/herdr/wire/codec.d.ts +36 -0
- package/dist/herdr/wire/codec.js +62 -0
- package/dist/herdr/wire/connection.d.ts +13 -0
- package/dist/herdr/wire/connection.js +65 -0
- package/dist/herdr/wire/dedicated.d.ts +2 -0
- package/dist/herdr/wire/dedicated.js +238 -0
- package/dist/herdr/wire/push-decoder.d.ts +2 -0
- package/dist/herdr/wire/push-decoder.js +40 -0
- package/dist/herdr/wire/rpc.d.ts +3 -0
- package/dist/herdr/wire/rpc.js +141 -0
- package/dist/herdr/wire/socket-path.d.ts +8 -0
- package/dist/herdr/wire/socket-path.js +23 -0
- package/dist/herdr/wire/types.d.ts +78 -0
- package/dist/herdr/wire/types.js +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +7 -0
- package/dist/read-only.d.ts +5 -0
- package/dist/read-only.js +6 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.js +6 -0
- package/dist/test-kit.d.ts +2 -0
- package/dist/test-kit.js +3 -0
- package/docs/api/events.md +43 -0
- package/docs/api/methods.md +232 -0
- package/package.json +112 -4
- package/schema/herdr-api.schema.json +10759 -0
- package/schema/herdr-version.json +5 -0
- package/skills/herdr-sdk/SKILL.md +75 -0
- package/skills/herdr-sdk/references/api-map.md +28 -0
- package/skills/herdr-sdk/references/errors.md +19 -0
- package/skills/herdr-sdk/references/recipes.md +171 -0
- package/skills/herdr-sdk/references/versions.md +7 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: herdr-sdk
|
|
3
|
+
description: Write TypeScript or JavaScript that talks to a running herdr server (spaces/workspaces, tabs, panes, coding agents) through the typed @stateforge-labs/herdr-sdk client over herdr's unix socket. Use when code imports @stateforge-labs/herdr-sdk, or the task is to list, watch, prompt, wait on or read herdr panes and agents from a script, plugin, daemon or test, or to fake herdr in tests, or to talk to herdr-core (hc) over its socket. Not for driving the herdr CLI by hand (hc skill) or the GUI window (herdr-app skill).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @stateforge-labs/herdr-sdk
|
|
7
|
+
|
|
8
|
+
Typed client for herdr's socket API. Zero runtime dependencies, Bun and Node 22.18+. Targets **@stateforge-labs/herdr-sdk 0.3.0 = herdr protocol 22 (herdr 0.9.5)**.
|
|
9
|
+
|
|
10
|
+
## First: check the version
|
|
11
|
+
|
|
12
|
+
Do not trust memory of this API. Before writing code:
|
|
13
|
+
|
|
14
|
+
1. Read `node_modules/@stateforge-labs/herdr-sdk/package.json`: `version` and `herdr.protocol` must match the line above. If they differ, this skill may be stale; read the package's `docs/api/methods.md` and `docs/api/events.md` (generated from the schema, always current) before trusting any name here.
|
|
15
|
+
2. After editing, run the type checker. Param and result types are generated, so a wrong field name fails at compile time.
|
|
16
|
+
|
|
17
|
+
## Pick the entry point (least power that works)
|
|
18
|
+
|
|
19
|
+
| Import | Function | Can do | Cannot |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `@stateforge-labs/herdr-sdk/read-only` | `connectReadOnly()` | structure reads (`*.list`, `*.get`, `session.snapshot`), long-poll waits, `events.subscribe`, `watch` | terminal content (opt in with `{ content: true }`), any write, generic `call()` |
|
|
22
|
+
| `@stateforge-labs/herdr-sdk/read-only` | `connectPrompt()` | structure reads + `agent.prompt` | everything else |
|
|
23
|
+
| `@stateforge-labs/herdr-sdk` | `connect()` | reads, content, UI changes, input, hooks, long-poll, `hold()` | destructive, server control, spawn/execute |
|
|
24
|
+
| `@stateforge-labs/herdr-sdk/admin` | `connectAdmin()` | all 102 methods | nothing |
|
|
25
|
+
| `@stateforge-labs/herdr-sdk/effect` | `Herdr` service + Layers | same as above as Effect (needs peer `effect` 4.1.2) | |
|
|
26
|
+
| `@stateforge-labs/herdr-sdk/test-kit` | `createFakeHerdr()` | in-process fake server for tests | production use |
|
|
27
|
+
| `@stateforge-labs/herdr-sdk/schema` | method table, param shapes | tooling that reflects on the API | |
|
|
28
|
+
| `@stateforge-labs/herdr-sdk/core` | `CoreLink`, `CoreEvents`, wire parsers, `hcCoreSocketPath()` | a client of the herdr-core (`hc`) socket: calls, the event feed with heartbeats, failures as `Unknown` values | the protocol-22 herdr socket |
|
|
29
|
+
| `@stateforge-labs/herdr-sdk/core/test-kit` | `FakeHcCore`, `setupTest(t)`, `openWorld()` | a scripted fake herdr-core for tests | production use |
|
|
30
|
+
|
|
31
|
+
Start with `connectReadOnly()`. Use `connectAdmin()` only for `*.close`, `server.*`, `agent.start`, `plugin.*`: say why in a comment.
|
|
32
|
+
|
|
33
|
+
## Minimal use
|
|
34
|
+
|
|
35
|
+
```ts run
|
|
36
|
+
// fake: agent.list {"type":"agent_list","agents":[{"agent_status":"idle","focused":false,"pane_id":"p_1","revision":1,"tab_id":"t_1","terminal_id":"term_1","workspace_id":"w_1","name":"reviewer"}]}
|
|
37
|
+
import { connect, HerdrError } from "@stateforge-labs/herdr-sdk"
|
|
38
|
+
|
|
39
|
+
const herdr = await connect() // HERDR_SOCKET, else ~/.config/herdr/herdr.sock; pings; checks protocol 22
|
|
40
|
+
try {
|
|
41
|
+
const { agents } = await herdr.agent.list({})
|
|
42
|
+
for (const agent of agents) console.log(agent.name, agent.agent_status)
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof HerdrError) console.error(error.code, error.message)
|
|
45
|
+
else throw error
|
|
46
|
+
} finally {
|
|
47
|
+
herdr.close()
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Methods are nested by their dotted wire name: `herdr.pane.list({})`, `herdr.agent.view.set({...})`. Every method takes one params object (`{}` when empty).
|
|
52
|
+
|
|
53
|
+
## Rules
|
|
54
|
+
|
|
55
|
+
- **Always** use the generated constants (`Method`, `EventKind`, `SubscriptionType`, `AgentStatus`, `WatchKind`, `ResultTag`), not raw strings: the types accept strings, but constants fail when herdr renames something.
|
|
56
|
+
- **Always** `close()` the client (or `await using` a held connection); a leaked client keeps the socket open.
|
|
57
|
+
- **Always** snapshot before you subscribe: a subscription delivers changes, not the present. `herdr.watch({ snapshot })` does snapshot, subscribe, replay and resubscribe for you.
|
|
58
|
+
- **Always** pass `timeout_ms` to long-poll methods (`agent.wait`, `events.wait`, `pane.wait_for_output`): herdr would wait forever, and the types require it on `agent.wait`.
|
|
59
|
+
- **Never** resend `agent.prompt` after a timeout: a timeout is not proof the prompt was not delivered.
|
|
60
|
+
- **Never** assume `uint64` fields above 2^53 survive; they are JSON numbers.
|
|
61
|
+
- **Never** import from the package's `dist/` or `src/` paths; only the entries above.
|
|
62
|
+
- **Never** point tests at a real herdr. Use `createFakeHerdr()`.
|
|
63
|
+
|
|
64
|
+
## Where to go next
|
|
65
|
+
|
|
66
|
+
| Task | Read |
|
|
67
|
+
|---|---|
|
|
68
|
+
| Wait for an agent, prompt it, read output, split panes, subscribe, watch, test | `references/recipes.md` |
|
|
69
|
+
| An error or exception, what to catch, retry or not | `references/errors.md` |
|
|
70
|
+
| Which method exists, its params and result | `references/api-map.md` (namespaces and where the full list lives) |
|
|
71
|
+
| Versions, upgrades, drift against the installed herdr | `references/versions.md` |
|
|
72
|
+
|
|
73
|
+
## Verify
|
|
74
|
+
|
|
75
|
+
Run the type checker, then the code against the fake (`references/recipes.md`, "Test it"). Only point at the real socket when the owner says so.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# API map
|
|
2
|
+
|
|
3
|
+
The authoritative list is generated and ships with the package: `docs/api/methods.md` and `docs/api/events.md`, or in code `import { METHODS } from "@stateforge-labs/herdr-sdk/schema"`. This page only orients you.
|
|
4
|
+
|
|
5
|
+
## Namespaces (method = `namespace.name`, called as `herdr.namespace.name(params)`)
|
|
6
|
+
|
|
7
|
+
`pane` (35, the largest: list, get, read, split, send_text, send_keys, wait_for_output, focus, close, ...), `agent` (12: list, get, read, start, prompt, wait, send_keys, rename, view.*), `plugin` (11), `workspace` (9), `tab` (7), `server` (6), `worktree` (4), `layout` (3), `integration` (3), `client` (3), `events` (subscribe, wait), `session` (snapshot), `ping`, plus single-method namespaces (`notification`, `popup`, `command`, `held`, ...).
|
|
8
|
+
|
|
9
|
+
## Capability classes (which entry has them)
|
|
10
|
+
|
|
11
|
+
| Class | Meaning | Examples | Entries |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| R-meta | read structure | `*.list`, `*.get`, `session.snapshot` | all |
|
|
14
|
+
| R-content | read terminal content | `pane.read`, `agent.read` | `connect`, `connectReadOnly({content:true})` |
|
|
15
|
+
| W | change UI or state | `*.rename`, `*.focus`, `pane.split` | `connect`, admin |
|
|
16
|
+
| I | input injection | `pane.send_text`, `agent.prompt` | `connect`, admin (prompt also `connectPrompt`) |
|
|
17
|
+
| H | hook reporter | `pane.report_*` | `connect`, admin |
|
|
18
|
+
| L | long-poll or stream | `agent.wait`, `events.wait`, `pane.wait_for_output`, `events.subscribe` | `connect`, admin, read-only (reads) |
|
|
19
|
+
| C | connection-bound | `client.window_title.set` | via `hold()` |
|
|
20
|
+
| X | spawns or executes | `agent.start`, `plugin.link`, `command.invoke` | admin only |
|
|
21
|
+
| D | destructive | `*.close`, `worktree.remove` | admin only |
|
|
22
|
+
| S | server control | `server.stop`, `server.reload_config` | admin only |
|
|
23
|
+
|
|
24
|
+
If a method you need is not on your client, it is deliberately out of that entry's class set. Widen the entry (with a stated reason), do not cast.
|
|
25
|
+
|
|
26
|
+
## Ids
|
|
27
|
+
|
|
28
|
+
Panes, tabs and workspaces are addressed by their ids (`p_1`-style strings from `*.list` or the snapshot). `agent.*` methods take a `target` (agent name or id).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Errors
|
|
2
|
+
|
|
3
|
+
Five Promise error classes, all exported from `@stateforge-labs/herdr-sdk`. Check with `instanceof`.
|
|
4
|
+
|
|
5
|
+
| Class | Meaning | Fields | Retry? |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| `HerdrError` | herdr received the call and refused it (error envelope) | `code`, `method`, `body`, `message` | No: fix the params or the target |
|
|
8
|
+
| `HerdrTransportError` | no usable answer: cannot connect, timeout, abort, socket closed, malformed line | `isTimeout`, `cause` | Reads: yes. `agent.prompt` and other input methods: **no**, delivery is unknown |
|
|
9
|
+
| `ProtocolMismatchError` | the server speaks another protocol than this client (`connect()` default is exact) | `expected`, `actual` | No: upgrade the package or run the matching herdr |
|
|
10
|
+
| `UnsupportedByServerError` | the method needs a server capability herdr did not advertise; nothing was sent | `method`, `capability` | No |
|
|
11
|
+
| `SubscriptionEnded` | the server closed a subscription connection | `method` | `watch()` resubscribes for you; with raw `events.subscribe` you resubscribe yourself and re-snapshot |
|
|
12
|
+
|
|
13
|
+
Notes:
|
|
14
|
+
|
|
15
|
+
- `connect()` rejects when there is no socket; the message names the path. Check that herdr is running and that `HERDR_SOCKET` points at the server you mean.
|
|
16
|
+
- A closed client rejects later calls with `HerdrTransportError` without contacting herdr; `close()` also ends its subscriptions, watch loops and held connections.
|
|
17
|
+
- The default per-request timeout is 10 s (`timeoutMs` option). Long-poll methods add their own `timeout_ms`.
|
|
18
|
+
- `onCall({ method, durationMs, outcome })` on `connect()` options is the metrics hook; `onUnknown(frame)` receives push frames of kinds this version does not know (herdr may add some): log and ignore them.
|
|
19
|
+
- Effect entry: the same errors are tagged: `HerdrRefused`, `HerdrUnavailable` (transport and subscription end; `isTimeout`), `ProtocolMismatch`, `HerdrUnsupported`.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Recipes
|
|
2
|
+
|
|
3
|
+
Every `ts` block below is type-checked against the package by `test/skill/skill.test.ts`, and blocks marked `ts run` are executed against the fake herdr. Lines starting `// fake:` are the harness script (what the server answers); they are not part of your code. Copy them as written; `HERDR_SOCKET` or the default socket path picks the server.
|
|
4
|
+
|
|
5
|
+
## List agents and what they are doing
|
|
6
|
+
|
|
7
|
+
```ts run
|
|
8
|
+
// fake: agent.list {"type":"agent_list","agents":[{"agent_status":"idle","focused":false,"pane_id":"p_1","revision":1,"tab_id":"t_1","terminal_id":"term_1","workspace_id":"w_1","name":"reviewer"}]}
|
|
9
|
+
import { connectReadOnly } from "@stateforge-labs/herdr-sdk/read-only"
|
|
10
|
+
|
|
11
|
+
const herdr = await connectReadOnly()
|
|
12
|
+
const { agents } = await herdr.agent.list({})
|
|
13
|
+
for (const agent of agents) console.log(agent.name, agent.agent_status, agent.pane_id)
|
|
14
|
+
herdr.close()
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Take the present state in one call
|
|
18
|
+
|
|
19
|
+
`session.snapshot` returns workspaces, tabs, panes, agents and layouts together. Prefer it to many `*.list` calls.
|
|
20
|
+
|
|
21
|
+
```ts run
|
|
22
|
+
// fake: session.snapshot {"type":"session_snapshot","snapshot":{"agents":[],"layouts":[],"panes":[],"protocol":22,"tabs":[],"version":"0.9.5","workspaces":[]}}
|
|
23
|
+
import { connectReadOnly } from "@stateforge-labs/herdr-sdk/read-only"
|
|
24
|
+
|
|
25
|
+
const herdr = await connectReadOnly()
|
|
26
|
+
const { snapshot } = await herdr.session.snapshot({})
|
|
27
|
+
console.log(snapshot.workspaces.length, snapshot.tabs.length, snapshot.panes.length)
|
|
28
|
+
herdr.close()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Wait for an agent to finish
|
|
32
|
+
|
|
33
|
+
The timeout is required in practice: without it herdr waits forever.
|
|
34
|
+
|
|
35
|
+
```ts run
|
|
36
|
+
// fake: agent.wait {"type":"agent_info","agent":{"agent_status":"idle","focused":false,"pane_id":"p_1","revision":1,"tab_id":"t_1","terminal_id":"term_1","workspace_id":"w_1","name":"reviewer"}}
|
|
37
|
+
import { connectReadOnly } from "@stateforge-labs/herdr-sdk/read-only"
|
|
38
|
+
import { AgentStatus } from "@stateforge-labs/herdr-sdk"
|
|
39
|
+
|
|
40
|
+
const herdr = await connectReadOnly()
|
|
41
|
+
const info = await herdr.agent.wait({ target: "reviewer", until: [AgentStatus.Done], timeout_ms: 60_000 })
|
|
42
|
+
console.log(info.agent.agent_status)
|
|
43
|
+
herdr.close()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Prompt an agent
|
|
47
|
+
|
|
48
|
+
`connectPrompt()` can read structure and prompt, nothing else.
|
|
49
|
+
|
|
50
|
+
```ts run
|
|
51
|
+
// fake: agent.prompt {"type":"ok"}
|
|
52
|
+
import { connectPrompt } from "@stateforge-labs/herdr-sdk/read-only"
|
|
53
|
+
import { HerdrError, HerdrTransportError } from "@stateforge-labs/herdr-sdk"
|
|
54
|
+
|
|
55
|
+
const herdr = await connectPrompt()
|
|
56
|
+
try {
|
|
57
|
+
await herdr.agent.prompt({ target: "reviewer", text: "run the tests" })
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (error instanceof HerdrError) console.error("refused", error.code, error.message)
|
|
60
|
+
else if (error instanceof HerdrTransportError) console.error("unknown whether delivered; do not resend blindly")
|
|
61
|
+
else throw error
|
|
62
|
+
}
|
|
63
|
+
herdr.close()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Read a pane's terminal output
|
|
67
|
+
|
|
68
|
+
Terminal content is a separate capability: `connect()` has it, `connectReadOnly()` needs `{ content: true }`.
|
|
69
|
+
|
|
70
|
+
```ts run
|
|
71
|
+
// fake: pane.read {"type":"pane_read","read":{"format":"text","pane_id":"p_1","revision":1,"source":"recent","tab_id":"t_1","text":"ok","truncated":false,"workspace_id":"w_1"}}
|
|
72
|
+
import { connectReadOnly } from "@stateforge-labs/herdr-sdk/read-only"
|
|
73
|
+
|
|
74
|
+
const herdr = await connectReadOnly({ content: true })
|
|
75
|
+
const { read } = await herdr.pane.read({ pane_id: "p_1", source: "recent", lines: 40 })
|
|
76
|
+
console.log(read)
|
|
77
|
+
herdr.close()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Wait for output to match
|
|
81
|
+
|
|
82
|
+
```ts run
|
|
83
|
+
// fake: pane.wait_for_output {"type":"ok"}
|
|
84
|
+
import { connect } from "@stateforge-labs/herdr-sdk"
|
|
85
|
+
|
|
86
|
+
const herdr = await connect()
|
|
87
|
+
await herdr.pane.wait_for_output({
|
|
88
|
+
pane_id: "p_1",
|
|
89
|
+
source: "recent",
|
|
90
|
+
match: { type: "regex", value: "tests? passed" },
|
|
91
|
+
timeout_ms: 30_000,
|
|
92
|
+
})
|
|
93
|
+
herdr.close()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Split a pane and run a command in it
|
|
97
|
+
|
|
98
|
+
`pane.split` is a write (`connect()`), not available on read-only clients.
|
|
99
|
+
|
|
100
|
+
```ts run
|
|
101
|
+
// fake: pane.split {"type":"ok"}
|
|
102
|
+
// fake: pane.send_text {"type":"ok"}
|
|
103
|
+
// fake: pane.send_keys {"type":"ok"}
|
|
104
|
+
import { connect } from "@stateforge-labs/herdr-sdk"
|
|
105
|
+
|
|
106
|
+
const herdr = await connect()
|
|
107
|
+
await herdr.pane.split({ direction: "right", target_pane_id: "p_1", cwd: "/tmp" })
|
|
108
|
+
await herdr.pane.send_text({ pane_id: "p_1", text: "echo hi" })
|
|
109
|
+
await herdr.pane.send_keys({ pane_id: "p_1", keys: ["Enter"] })
|
|
110
|
+
herdr.close()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## React to events (subscribe)
|
|
114
|
+
|
|
115
|
+
A subscription is an async iterable of frames. Subscribe, loop, `close()` when done.
|
|
116
|
+
|
|
117
|
+
```ts run
|
|
118
|
+
import { connectReadOnly } from "@stateforge-labs/herdr-sdk/read-only"
|
|
119
|
+
import { EventKind, SubscriptionType } from "@stateforge-labs/herdr-sdk/read-only"
|
|
120
|
+
|
|
121
|
+
const herdr = await connectReadOnly()
|
|
122
|
+
const subscription = await herdr.events.subscribe([
|
|
123
|
+
{ type: SubscriptionType.PaneCreated },
|
|
124
|
+
{ type: SubscriptionType.PaneClosed },
|
|
125
|
+
])
|
|
126
|
+
const stop = setTimeout(() => subscription.close(), 50)
|
|
127
|
+
for await (const frame of subscription) {
|
|
128
|
+
if (frame.event === EventKind.PaneClosed) break
|
|
129
|
+
}
|
|
130
|
+
clearTimeout(stop)
|
|
131
|
+
subscription.close()
|
|
132
|
+
herdr.close()
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Keep a live view (watch)
|
|
136
|
+
|
|
137
|
+
`watch` takes a snapshot, subscribes, replays frames that arrived in between, and resubscribes with backoff after a drop. The first item is always `Live`. Frames can be replayed, so make your fold idempotent.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { connectReadOnly } from "@stateforge-labs/herdr-sdk/read-only"
|
|
141
|
+
import { WatchKind } from "@stateforge-labs/herdr-sdk"
|
|
142
|
+
|
|
143
|
+
const herdr = await connectReadOnly()
|
|
144
|
+
const view = herdr.watch({ snapshot: () => herdr.session.snapshot({}) })
|
|
145
|
+
for await (const change of view) {
|
|
146
|
+
if (change.kind === WatchKind.Live) console.log("snapshot", change.snapshot.snapshot.panes.length)
|
|
147
|
+
else console.log("frame", change.frame)
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Test it (no real herdr)
|
|
152
|
+
|
|
153
|
+
The fake answers `ping`; every other method must be scripted with `fake.reply(method, result)`, otherwise herdr-style `unknown_method` is returned. It also lets you script failures, silence and pushes.
|
|
154
|
+
|
|
155
|
+
```ts run
|
|
156
|
+
import { connect, Method, ResultTag } from "@stateforge-labs/herdr-sdk"
|
|
157
|
+
import { createFakeHerdr } from "@stateforge-labs/herdr-sdk/test-kit"
|
|
158
|
+
|
|
159
|
+
const fake = await createFakeHerdr()
|
|
160
|
+
fake.reply(Method.AgentList, { type: ResultTag.AgentList, agents: [] })
|
|
161
|
+
const herdr = await connect({ socketPath: fake.path })
|
|
162
|
+
|
|
163
|
+
const { agents } = await herdr.agent.list({})
|
|
164
|
+
if (agents.length !== 0) throw new Error("expected no agents")
|
|
165
|
+
if (fake.receivedMethods().join() !== [Method.Ping, Method.AgentList].join()) throw new Error("unexpected calls")
|
|
166
|
+
|
|
167
|
+
herdr.close()
|
|
168
|
+
await fake.stop()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Other fake controls: `fail(method, error)`, `silent(method)` (for timeout tests), `replySequence`, `push(frame)`, `closeSubscribers()`, `subscriberCount()`, `capabilities`, `requests()`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Versions, upgrades, drift
|
|
2
|
+
|
|
3
|
+
- The package version follows herdr: a protocol change, or a removed, renamed or narrowed API, is a major; added methods and events are a minor; everything else is a patch. The package.json `herdr` field has `protocol`, `schemaVersion` and `testedWith`.
|
|
4
|
+
- `connect()` checks the server's protocol and rejects with `ProtocolMismatchError` unless it matches exactly (`protocolPolicy: ProtocolPolicy.Minimum` accepts a newer server).
|
|
5
|
+
- The generated reference ships with the package: `docs/api/methods.md` (102 methods, params type, result tag, capability class, long-poll, server capability gate) and `docs/api/events.md` (event kinds and subscription types). Read these instead of guessing a name.
|
|
6
|
+
- `herdr-drift` (bin) compares the committed schema with the installed herdr: exit 0 clean, 1 drift, 2 usage, 3 herdr not found or unreadable schema (3 means "could not look", never green).
|
|
7
|
+
- When herdr upgrades: run `herdr-drift`; if only additions, bump the minor; if it reports removals, treat it as a major and re-read `docs/api/methods.md`.
|