@opencode-cockpit/client 0.2.1 → 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/README.md +122 -5
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,11 +1,128 @@
|
|
|
1
1
|
# @opencode-cockpit/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The typed client for `cockpitd`.** Connects, and if nothing is listening, starts the daemon first.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every half of every Cockpit plugin talks to the daemon through this: the agent tools in OpenCode's
|
|
6
|
+
server thread, and the panel in its interface thread. It hides the parts that are easy to get wrong —
|
|
7
|
+
spawning, reconnecting, version skew, subscriptions — so a capability is left writing calls.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
> Installing the plugin is what you usually want. This package matters if you are building your own
|
|
10
|
+
> front end for Cockpit, or a capability of your own.
|
|
11
|
+
>
|
|
12
|
+
> ```sh
|
|
13
|
+
> opencode plugin opencode-cockpit --global
|
|
14
|
+
> ```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Using it
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { CockpitClient } from "@opencode-cockpit/client"
|
|
22
|
+
|
|
23
|
+
const client = new CockpitClient({
|
|
24
|
+
client: { name: "my-tool", version: "1.0.0", pid: process.pid },
|
|
25
|
+
spawn: { entry: daemonEntry(), execPath: process.execPath },
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const shell = await client.call("shell.start", {
|
|
29
|
+
command: "npm",
|
|
30
|
+
args: ["run", "dev"],
|
|
31
|
+
cwd: process.cwd(),
|
|
32
|
+
owner: { project: process.cwd(), session: "sess_1" },
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
await client.call("shell.wait", { id: shell.id, until: { port: 5173 }, timeoutMs: 30_000 })
|
|
36
|
+
const page = await client.call("shell.read", { id: shell.id, tail: 20 })
|
|
37
|
+
console.log(page.lines.map((l) => `${l.n}| ${l.text}`).join("\n"))
|
|
9
38
|
```
|
|
10
39
|
|
|
11
|
-
|
|
40
|
+
`call` is typed end to end from the protocol contract: the method name completes, the params are
|
|
41
|
+
checked, and the result comes back with its real shape. An unknown method is a compile error, not a
|
|
42
|
+
runtime surprise.
|
|
43
|
+
|
|
44
|
+
## Events
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const off = client.on("shell.exited", (info) => {
|
|
48
|
+
console.log(`${info.id} ended: ${info.status}`)
|
|
49
|
+
})
|
|
50
|
+
// …later
|
|
51
|
+
off()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Subscriptions are restored automatically after a reconnect, so a dropped socket does not silently
|
|
55
|
+
stop the messages.
|
|
56
|
+
|
|
57
|
+
## Starting the daemon
|
|
58
|
+
|
|
59
|
+
Pass `spawn` and the client starts `cockpitd` when nothing is listening, waits for the socket, and
|
|
60
|
+
connects. A lock file keeps two clients racing at the same moment from starting two daemons.
|
|
61
|
+
|
|
62
|
+
Inside OpenCode, `process.execPath` is the OpenCode binary rather than Bun, so the client runs the
|
|
63
|
+
daemon with `BUN_BE_BUN=1` against OpenCode's embedded runtime — no separate Bun install.
|
|
64
|
+
|
|
65
|
+
## Version skew
|
|
66
|
+
|
|
67
|
+
The daemon is long-lived and shared, so the code running inside it can be older than the plugin that
|
|
68
|
+
just connected. `expectedBuild` is the build id of the daemon code shipped with your package:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { daemonBuildId } from "@opencode-cockpit/protocol"
|
|
72
|
+
|
|
73
|
+
new CockpitClient({
|
|
74
|
+
client: { name: "my-tool", version: pkg.version },
|
|
75
|
+
spawn: { entry, execPath: process.execPath },
|
|
76
|
+
expectedBuild: daemonBuildId(entry, daemonPkg.version),
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- **Newer client, idle daemon** — the daemon is replaced silently.
|
|
81
|
+
- **Newer client, busy daemon** — kept, and reported through `onOutdated` so you can say so and offer
|
|
82
|
+
a restart. Running shells are never killed to make an upgrade convenient.
|
|
83
|
+
- **Older client** — connects as-is. Upgrades only go forward, or two versions would take turns
|
|
84
|
+
replacing each other.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
client.onOutdated((info) => {
|
|
88
|
+
if (info) console.warn(`daemon runs ${info.running}, this build expects ${info.expected}`)
|
|
89
|
+
})
|
|
90
|
+
await client.restartDaemon() // when the user is ready
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## One capability, loaded twice
|
|
94
|
+
|
|
95
|
+
A capability can arrive through the bundle and on its own at the same time. `claimFeature` makes the
|
|
96
|
+
first one win and gives the second a message worth showing:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { claimFeature, duplicateFeatureMessage } from "@opencode-cockpit/client"
|
|
100
|
+
|
|
101
|
+
const claim = claimFeature("shell", "opencode-cockpit")
|
|
102
|
+
if (!claim) return console.warn(duplicateFeatureMessage("shell"))
|
|
103
|
+
// …later
|
|
104
|
+
claim.release()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## API
|
|
108
|
+
|
|
109
|
+
| Export | What it is |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `CockpitClient` | The client: `call`, `on`, `onOutdated`, `onState`, `restartDaemon`, `close`, `daemon` |
|
|
112
|
+
| `CockpitClientOptions` | `client`, `paths`, `spawn`, `expectedBuild` |
|
|
113
|
+
| `OutdatedDaemon` | `{ running, expected }` |
|
|
114
|
+
| `compareBuilds` | Orders two build ids; equal versions with different hashes count as newer |
|
|
115
|
+
| `claimFeature`, `duplicateFeatureMessage`, `FeatureClaim` | First-one-wins guard for duplicate loads |
|
|
116
|
+
| `SpawnOptions` | `entry`, `execPath`, `env` |
|
|
117
|
+
|
|
118
|
+
## Requirements
|
|
119
|
+
|
|
120
|
+
Bun ≥ 1.3.5. macOS and Linux.
|
|
121
|
+
|
|
122
|
+
## More
|
|
123
|
+
|
|
124
|
+
[Architecture](https://codestz.github.io/opencode-cockpit/platform/architecture/) ·
|
|
125
|
+
[Daemon](../daemon) · [Protocol](../protocol) ·
|
|
126
|
+
[Repository](https://github.com/Codestz/opencode-cockpit)
|
|
127
|
+
|
|
128
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencode-cockpit/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Auto-spawning, reconnecting, typed client for cockpitd",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
".": {
|
|
24
24
|
"types": "./types/index.d.ts",
|
|
25
25
|
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./feature": {
|
|
28
|
+
"types": "./types/feature.d.ts",
|
|
29
|
+
"default": "./dist/feature.js"
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
32
|
"files": [
|
|
@@ -35,10 +39,10 @@
|
|
|
35
39
|
"access": "public"
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
|
-
"@opencode-cockpit/protocol": "0.
|
|
42
|
+
"@opencode-cockpit/protocol": "0.3.0"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
41
|
-
"@opencode-cockpit/daemon": "0.
|
|
45
|
+
"@opencode-cockpit/daemon": "0.3.0"
|
|
42
46
|
},
|
|
43
47
|
"engines": {
|
|
44
48
|
"bun": ">=1.3.5"
|