@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.
Files changed (2) hide show
  1. package/README.md +122 -5
  2. package/package.json +7 -3
package/README.md CHANGED
@@ -1,11 +1,128 @@
1
1
  # @opencode-cockpit/client
2
2
 
3
- Typed client for cockpitd. Starts the daemon when needed (one per machine, even under concurrent first calls), reconnects and restores subscriptions, replays read-only calls after a lost connection, and replaces a daemon running outdated code when it is idle.
3
+ **The typed client for `cockpitd`.** Connects, and if nothing is listening, starts the daemon first.
4
4
 
5
- Part of [opencode-cockpit](https://github.com/Codestz/opencode-cockpit). Install the plugin, not this package, unless you are building your own front end:
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
- ```sh
8
- opencode plugin opencode-cockpit --global
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
- Requires Bun 1.3.5 (OpenCode's embedded runtime qualifies). License: MIT.
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.2.1",
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.2.1"
42
+ "@opencode-cockpit/protocol": "0.3.0"
39
43
  },
40
44
  "devDependencies": {
41
- "@opencode-cockpit/daemon": "0.2.1"
45
+ "@opencode-cockpit/daemon": "0.3.0"
42
46
  },
43
47
  "engines": {
44
48
  "bun": ">=1.3.5"