mindwire 0.1.0 → 0.1.1
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/README.md +191 -0
- package/dist/catalog/index.d.ts +41 -0
- package/dist/client.d.ts +476 -0
- package/dist/daemon-binary.d.ts +13 -0
- package/dist/embedded.d.ts +14 -0
- package/dist/errors.d.ts +46 -0
- package/dist/http.d.ts +97 -0
- package/dist/index.cjs +2297 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +2260 -0
- package/dist/index.js.map +1 -0
- package/dist/run.d.ts +97 -0
- package/dist/sse.d.ts +13 -0
- package/dist/target/container.d.ts +60 -0
- package/dist/target/docker.d.ts +114 -0
- package/dist/target/host.d.ts +81 -0
- package/dist/target/index.d.ts +76 -0
- package/dist/target/oblien.d.ts +112 -0
- package/dist/target/ssh.d.ts +139 -0
- package/dist/types.d.ts +909 -0
- package/dist/version.d.ts +1 -0
- package/package.json +72 -3
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# mindwire
|
|
2
|
+
|
|
3
|
+
A typed TypeScript client for the **mindwire daemon** — one SDK for every coding-agent harness
|
|
4
|
+
(Claude Code, Codex, Copilot CLI, opencode). The daemon normalizes each agent to a single
|
|
5
|
+
protocol; this package is a thin, dependency-free client over its REST + SSE surface.
|
|
6
|
+
|
|
7
|
+
- **Embedded by default.** On a server runtime (Node/Bun/Deno) the SDK auto-spawns the bundled
|
|
8
|
+
`mindwired` daemon on loopback — nothing to install or deploy. Pass a `target` to run the daemon
|
|
9
|
+
somewhere else: `remote(url)`, or `ssh(...)` / `docker(...)` / `oblien(...)` to provision one.
|
|
10
|
+
- **Zero runtime dependencies.** Uses the platform `fetch` and WHATWG streams — Node 18+, Bun, Deno, browsers.
|
|
11
|
+
- **One protocol, any agent.** Pick the harness per client or per call with `agent`.
|
|
12
|
+
- **Streaming-first.** A turn is an async-iterable of unified events (`text`, `thinking`, `tool_use`, `result`, …).
|
|
13
|
+
- **Faithful types.** Every shape mirrors the daemon's Go wire structs 1:1.
|
|
14
|
+
|
|
15
|
+
> 📚 **Guides & architecture:** [mindwire.sh/docs](https://mindwire.sh/docs). This page is the SDK reference.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm i mindwire
|
|
21
|
+
# or: bun add mindwire / pnpm add mindwire
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { Mindwire } from "mindwire";
|
|
28
|
+
|
|
29
|
+
// Embedded (default): auto-spawns the bundled daemon on first use. No server to run.
|
|
30
|
+
const mw = new Mindwire({ agent: "claude-code" });
|
|
31
|
+
|
|
32
|
+
// What can this daemon run?
|
|
33
|
+
const { agents } = await mw.catalog();
|
|
34
|
+
|
|
35
|
+
// Start a turn and stream the unified event feed.
|
|
36
|
+
const run = await mw.turn({ chatId: "chat-1", message: "add a /healthz endpoint" });
|
|
37
|
+
|
|
38
|
+
for await (const ev of run) {
|
|
39
|
+
switch (ev.type) {
|
|
40
|
+
case "text": process.stdout.write(ev.text ?? ""); break;
|
|
41
|
+
case "tool_use": console.log(`\n↳ ${ev.tool?.name}`, ev.tool?.input); break;
|
|
42
|
+
case "result": console.log(`\n✓ $${ev.result?.costUsd} · ${ev.result?.numTurns} turns`); break;
|
|
43
|
+
case "error": console.error("error:", ev.error); break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Await a turn instead of streaming
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const run = await mw.turn({ chatId: "chat-1", message: "run the tests" });
|
|
52
|
+
const { result } = await run.wait(); // throws RunFailedError on error/cancelled
|
|
53
|
+
console.log(result?.text);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Cancel a turn
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const run = await mw.turn({ chatId: "chat-1", message: "big refactor" });
|
|
60
|
+
setTimeout(() => run.cancel(), 5_000);
|
|
61
|
+
for await (const ev of run) { /* … */ }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Targets — where the daemon runs
|
|
65
|
+
|
|
66
|
+
**One `new Mindwire` = one instance = one daemon = one environment.** Every agent that instance runs
|
|
67
|
+
(via `agent`, `withAgent`, or a per-call `agent`) shares that one daemon's filesystem and state. To
|
|
68
|
+
isolate agents, create a **second** `Mindwire` with its own `target` — a separate daemon. Omit
|
|
69
|
+
`target` for the zero-config embedded default.
|
|
70
|
+
|
|
71
|
+
The `target` option is a factory; pick where the daemon runs and how the SDK reaches it:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { Mindwire, local, remote, ssh, docker, oblien } from "mindwire";
|
|
75
|
+
|
|
76
|
+
// local (default) — Node/Bun/Deno. Auto-spawns the bundled `mindwired` on a loopback port on the
|
|
77
|
+
// first call. Clients with the same config share one daemon; distinct configs get distinct daemons.
|
|
78
|
+
new Mindwire({ agent: "claude-code" }); // zero-config
|
|
79
|
+
new Mindwire({ target: local({ cwd: "/path/to/repo", statePath: ".mindwire-state.json", bin: "/opt/mindwired" }) });
|
|
80
|
+
|
|
81
|
+
// remote — connect to a daemon already running over HTTP. Required in the browser/edge.
|
|
82
|
+
new Mindwire({ target: remote("https://mindwire.yourco.com", { token: process.env.MINDWIRE_TOKEN }) });
|
|
83
|
+
|
|
84
|
+
// ssh — provision + run the daemon on a bare box; the SDK reaches it over a local port-forward tunnel.
|
|
85
|
+
new Mindwire({ target: ssh({ host: "box.internal", username: "root" }) });
|
|
86
|
+
|
|
87
|
+
// docker — run in a container (local socket or a remote engine via `engine`).
|
|
88
|
+
new Mindwire({ target: docker({ image: "my/agent-image" }) });
|
|
89
|
+
|
|
90
|
+
// oblien — provision an Oblien sandbox and route through its gateway.
|
|
91
|
+
new Mindwire({ target: oblien({ clientId: process.env.OBLIEN_CLIENT_ID, clientSecret: process.env.OBLIEN_CLIENT_SECRET }) });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Any object implementing `Target` can also be passed as `target` (bring-your-own).
|
|
95
|
+
|
|
96
|
+
### Provisioning logs & `ensure()`
|
|
97
|
+
|
|
98
|
+
`ssh` / `docker` / `oblien` prep the box on connect — upload the daemon binary, launch it, health-poll
|
|
99
|
+
until ready. Pass a `logger` to watch each step, and `await mw.ensure()` to provision eagerly instead
|
|
100
|
+
of lazily on the first request (idempotent — it awaits the same work the first call would):
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const mw = new Mindwire({
|
|
104
|
+
target: ssh({ host: "box.internal", username: "root" }),
|
|
105
|
+
logger: (e) => console.log(`[${e.phase}] ${e.message}`), // connect · probe · upload · launch · ready · skip · error
|
|
106
|
+
});
|
|
107
|
+
await mw.ensure(); // resolves once the daemon is healthy; then turns stream as usual
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Binary discovery for the `local` daemon, in order: the `bin` option → `$MINDWIRE_DAEMON` →
|
|
111
|
+
the matching prebuilt package (`mindwire-daemon-<platform>-<arch>`, installed automatically as an
|
|
112
|
+
optional dependency) → a binary bundled next to the SDK (`bin/mindwired-<platform>-<arch>`) →
|
|
113
|
+
`mindwired` on `PATH`. For local dev without the prebuilt package, set `$MINDWIRE_DAEMON` to a
|
|
114
|
+
`go build`-produced binary.
|
|
115
|
+
|
|
116
|
+
`ssh` / `docker` / `oblien` require an optional peer (`ssh2` / `dockerode` / `oblien`) — install only
|
|
117
|
+
the one you use. Importing `mindwire` never loads them.
|
|
118
|
+
|
|
119
|
+
## Switching harnesses
|
|
120
|
+
|
|
121
|
+
Every agent-scoped call takes an optional `agent`, and `withAgent` scopes a whole client:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
await mw.agent({ agent: "codex" }); // one-off override
|
|
125
|
+
const cx = mw.withAgent("codex"); // scoped client, shared transport
|
|
126
|
+
const info = await cx.agent();
|
|
127
|
+
console.log(info.capabilities.protocol); // "cli" for claude/codex
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Auth (step-flow)
|
|
131
|
+
|
|
132
|
+
The daemon declares auth methods per agent; the client walks a generic `begin → step → status` flow.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const methods = await mw.auth.methods(); // e.g. [{ id: "apiKey", … }, { id: "login", interactive: true }]
|
|
136
|
+
|
|
137
|
+
// API-key method:
|
|
138
|
+
await mw.auth.begin("apiKey");
|
|
139
|
+
await mw.auth.step({ apiKey: "sk-ant-…" });
|
|
140
|
+
|
|
141
|
+
// Interactive login: begin() returns a URL; poll step() until complete.
|
|
142
|
+
let state = await mw.auth.begin("login");
|
|
143
|
+
while (state.status === "pending") {
|
|
144
|
+
console.log("open:", state.url);
|
|
145
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
146
|
+
state = await mw.auth.step({});
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API surface
|
|
151
|
+
|
|
152
|
+
| Method | Endpoint |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `mw.health()` | `GET /healthz` |
|
|
155
|
+
| `mw.catalog()` | `GET /catalog` |
|
|
156
|
+
| `mw.agent(scoped?)` | `GET /agent` |
|
|
157
|
+
| `mw.doctor(scoped?)` | `GET /doctor` |
|
|
158
|
+
| `mw.setup() / update() / setupStatus()` | `POST /setup` · `POST /update` · `GET /setup` |
|
|
159
|
+
| `mw.getConfig() / setConfig(values)` | `GET /config` · `PUT /config` |
|
|
160
|
+
| `mw.auth.methods() / begin() / step() / status()` | `/auth/*` |
|
|
161
|
+
| `mw.chats()` | `GET /chats` |
|
|
162
|
+
| `mw.messages(chatId, { limit, before })` | `GET /chats/{id}/messages` |
|
|
163
|
+
| `mw.latestRun(chatId)` | `GET /chats/{id}/run` |
|
|
164
|
+
| `mw.turn({ chatId, message, cwd? })` → `Run` | `POST /turns` |
|
|
165
|
+
| `mw.run(id)` → `Run` | `GET /runs/{id}` |
|
|
166
|
+
| `run.stream()` / `for await (…of run)` | `GET /runs/{id}/stream` (SSE) |
|
|
167
|
+
| `run.cancel()` | `POST /runs/{id}/cancel` |
|
|
168
|
+
| `run.wait()` / `run.refresh()` | stream to completion / re-fetch |
|
|
169
|
+
| `mw.getNotifyConfig() / setNotifyConfig()` | `/notify/config` |
|
|
170
|
+
| `mw.notifications({ signal })` | `GET /notify/stream` (SSE) |
|
|
171
|
+
|
|
172
|
+
Agent-scoped methods accept `{ agent }` to override the client default. Errors are thrown as
|
|
173
|
+
`ApiError` (with `.status` and parsed `.body`); a failed run awaited via `run.wait()` throws
|
|
174
|
+
`RunFailedError`.
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
bun install
|
|
180
|
+
bun run build # tsup → dist (ESM + CJS + .d.ts)
|
|
181
|
+
bun run typecheck # tsc --noEmit
|
|
182
|
+
bun test # client + SSE parser tests (mocked fetch)
|
|
183
|
+
bun run build:daemon # cross-compile mindwired → npm/mindwire-daemon-<platform>-<arch>/ (needs Go)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`build:daemon` produces the per-platform daemon packages and syncs the main package's
|
|
187
|
+
`optionalDependencies`. **Release order:** publish every `npm/mindwire-daemon-*` package first,
|
|
188
|
+
then publish `mindwire` — its optional dependencies must already exist on the registry at the
|
|
189
|
+
same version.
|
|
190
|
+
|
|
191
|
+
License: Apache-2.0.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The models.dev catalog — the reference list of which providers and models exist.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately NOT bundled: models.dev is a hosted JSON API and staying its client (rather than
|
|
5
|
+
* embedding a snapshot) is the whole point — the list is always current, and the SDK stays a thin catalog
|
|
6
|
+
* client with no committed multi-megabyte blob. The raw feed (`https://models.dev/api.json`, ~2 MB across
|
|
7
|
+
* ~190 providers) is fetched once per process, projected to {@link CatalogProvider}/{@link ModelInfo}, and
|
|
8
|
+
* memoized with a soft TTL. Uses `globalThis.fetch`, so it works in Node and the browser.
|
|
9
|
+
*
|
|
10
|
+
* This is pure catalog data — "which models exist" — and knows nothing about the daemon, a selected agent,
|
|
11
|
+
* or which providers are configured/authenticated. The daemon's job is only to relay a chosen provider's
|
|
12
|
+
* settings to the harness; the catalog lives here.
|
|
13
|
+
*/
|
|
14
|
+
import type { CatalogProvider, ModelInfo } from "../types.js";
|
|
15
|
+
/** The canonical models.dev catalog endpoint. */
|
|
16
|
+
export declare const MODELS_DEV_URL = "https://models.dev/api.json";
|
|
17
|
+
/** Options accepted by every catalog function. All optional; defaults hit models.dev via global fetch. */
|
|
18
|
+
export interface CatalogOptions {
|
|
19
|
+
/** Override the endpoint (e.g. a mirror or a pinned snapshot). Defaults to {@link MODELS_DEV_URL}. */
|
|
20
|
+
url?: string;
|
|
21
|
+
/** Override the fetch implementation (tests, a proxy transport). Defaults to `globalThis.fetch`. */
|
|
22
|
+
fetch?: typeof globalThis.fetch;
|
|
23
|
+
/** Abort signal for the underlying request. */
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Load (and memoize) the full models.dev catalog, projected to {@link CatalogProvider}[] sorted by name.
|
|
28
|
+
* Cached in-process for {@link TTL_MS}; concurrent calls share one in-flight request. Throws if the fetch
|
|
29
|
+
* fails or returns a non-2xx — callers (and the console) surface that as an error state.
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadCatalog(opts?: CatalogOptions): Promise<CatalogProvider[]>;
|
|
32
|
+
/** Every provider in the models.dev catalog, each with its models, sorted by provider name. */
|
|
33
|
+
export declare function catalogProviders(opts?: CatalogOptions): Promise<CatalogProvider[]>;
|
|
34
|
+
/** One provider by id (e.g. `"openai"`), or `undefined` if the catalog has no such provider. */
|
|
35
|
+
export declare function catalogProvider(id: string, opts?: CatalogOptions): Promise<CatalogProvider | undefined>;
|
|
36
|
+
/** Flat list of models — all providers, or just one when `provider` is given. */
|
|
37
|
+
export declare function catalogModels(provider?: string, opts?: CatalogOptions): Promise<ModelInfo[]>;
|
|
38
|
+
/** A single model by (provider, id), or `undefined` if absent. */
|
|
39
|
+
export declare function lookupModel(provider: string, id: string, opts?: CatalogOptions): Promise<ModelInfo | undefined>;
|
|
40
|
+
/** Drop the in-process cache so the next call re-fetches. Mainly for tests and manual refresh. */
|
|
41
|
+
export declare function clearCatalogCache(): void;
|