@scrylog/opencode-plugin 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Joel D. Martinez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # @scrylog/opencode-plugin
2
+
3
+ `@scrylog/opencode-plugin` is an OpenCode plugin that sends session and permission events to the `scrylog` daemon webhook in real time.
4
+
5
+ This is the recommended OpenCode integration when `scrylog` is running in `hooks` mode.
6
+
7
+ ## When To Use This Plugin
8
+
9
+ Use this plugin when:
10
+
11
+ - you run OpenCode inside `tmux`
12
+ - you want `scrylog` to receive immediate session updates
13
+ - you want the best available correlation between OpenCode sessions and `tmux` panes
14
+ - you want `hooks` mode, which is the recommended OpenCode mode in `scrylog`
15
+
16
+ Do not use this plugin as your primary integration when:
17
+
18
+ - you are using `opencode.mode: "sse"`
19
+ - you want standalone-only OpenCode monitoring through `opencode --port`, `opencode serve`, or `oc`
20
+
21
+ In `sse` mode, the `scrylog` daemon accepts the HTTP request but ignores the payload and returns:
22
+
23
+ ```json
24
+ { "ok": true, "skipped": "opencode-mode-sse" }
25
+ ```
26
+
27
+ ## What The Plugin Does
28
+
29
+ The plugin listens for OpenCode session and permission events and forwards them to:
30
+
31
+ ```text
32
+ http://127.0.0.1:7788/api/webhook/opencode
33
+ ```
34
+
35
+ If OpenCode is running inside `tmux`, the plugin also tries to write the current OpenCode session ID into the pane option:
36
+
37
+ ```text
38
+ @opencode-session
39
+ ```
40
+
41
+ That gives `scrylog` an exact signal for correlating the OpenCode session with the `tmux` pane.
42
+
43
+ ## Requirements
44
+
45
+ - `@scrylog/cli` installed and reachable through the `scrylog` command
46
+ - `scrylogd` running
47
+ - OpenCode installed
48
+ - `opencode.mode` set to `hooks` in `scrylog`
49
+ - `tmux` installed if you want pane correlation
50
+
51
+ ## Installation
52
+
53
+ ### 1. Install the plugin
54
+
55
+ ```bash
56
+ bun install -g @scrylog/opencode-plugin
57
+ ```
58
+
59
+ You can also use npm:
60
+
61
+ ```bash
62
+ npm install -g @scrylog/opencode-plugin
63
+ ```
64
+
65
+ ### 2. Configure `scrylog` for hooks mode
66
+
67
+ Create or update `~/.config/scrylog/config.json`:
68
+
69
+ ```json
70
+ {
71
+ "opencode": {
72
+ "mode": "hooks"
73
+ }
74
+ }
75
+ ```
76
+
77
+ You can also do this from the main operator CLI:
78
+
79
+ ```bash
80
+ scrylog setup
81
+ ```
82
+
83
+ Choose OpenCode, then choose `hooks` mode.
84
+
85
+ ### 3. Enable the plugin in OpenCode
86
+
87
+ Create or update `~/.config/opencode/opencode.json`:
88
+
89
+ ```json
90
+ {
91
+ "plugin": ["@scrylog/opencode-plugin"]
92
+ }
93
+ ```
94
+
95
+ ### 4. Start the daemon
96
+
97
+ ```bash
98
+ scrylog daemon install
99
+ scrylog daemon start
100
+ ```
101
+
102
+ Or run the daemon manually in the foreground:
103
+
104
+ ```bash
105
+ scrylogd
106
+ ```
107
+
108
+ ### 5. Start OpenCode
109
+
110
+ Start OpenCode normally. The plugin loads automatically.
111
+
112
+ ### 6. Verify it works
113
+
114
+ Open another terminal and check the daemon:
115
+
116
+ ```bash
117
+ curl http://127.0.0.1:7788/api/sessions
118
+ ```
119
+
120
+ You should see OpenCode sessions appear when they start receiving events.
121
+
122
+ You can also run:
123
+
124
+ ```bash
125
+ scrylog doctor
126
+ ```
127
+
128
+ That confirms local daemon reachability and current OpenCode mode.
129
+
130
+ ## Configuration
131
+
132
+ The plugin uses one environment variable:
133
+
134
+ | Variable | Default | Purpose |
135
+ | --- | --- | --- |
136
+ | `SCRYLOG_URL` | `http://127.0.0.1:7788/api/webhook/opencode` | Full webhook URL for the `scrylog` daemon |
137
+
138
+ ### Example: custom daemon port
139
+
140
+ ```bash
141
+ SCRYLOG_URL=http://127.0.0.1:9000/api/webhook/opencode opencode
142
+ ```
143
+
144
+ If you also moved the `scrylog` API port, make sure your `scrylog` config matches it:
145
+
146
+ ```json
147
+ {
148
+ "api": {
149
+ "tcpEnabled": true,
150
+ "host": "127.0.0.1",
151
+ "port": 9000
152
+ }
153
+ }
154
+ ```
155
+
156
+ ### Example: set it in your shell profile
157
+
158
+ ```bash
159
+ export SCRYLOG_URL=http://127.0.0.1:9000/api/webhook/opencode
160
+ ```
161
+
162
+ ## Supported Events
163
+
164
+ The plugin forwards these OpenCode events:
165
+
166
+ | Event | Meaning |
167
+ | --- | --- |
168
+ | `session.created` | New session created |
169
+ | `session.updated` | Session metadata changed |
170
+ | `session.deleted` | Session deleted |
171
+ | `session.status` | State changed, such as `busy`, `idle`, or `retry` |
172
+ | `session.idle` | Explicit idle signal |
173
+ | `session.error` | Session error |
174
+ | `permission.updated` | Permission prompt state updated |
175
+ | `permission.asked` | Permission prompt shown |
176
+ | `permission.replied` | Permission prompt answered |
177
+
178
+ ## Request Format
179
+
180
+ Each event is sent as:
181
+
182
+ - `POST`
183
+ - `Content-Type: application/json`
184
+
185
+ Example payload:
186
+
187
+ ```json
188
+ {
189
+ "event": "session.status",
190
+ "directory": "/Users/you/projects/my-app",
191
+ "sessionID": "ses_123",
192
+ "status": {
193
+ "type": "busy"
194
+ }
195
+ }
196
+ ```
197
+
198
+ For `session.created` and `session.updated`, OpenCode may also include richer `info` payloads such as title, timestamps, and `parentID`.
199
+
200
+ ## Hooks Mode Vs SSE Mode
201
+
202
+ ### In `hooks` mode
203
+
204
+ The daemon uses plugin webhook events as the primary OpenCode signal.
205
+
206
+ What you get:
207
+
208
+ - immediate OpenCode state updates
209
+ - permission prompt visibility
210
+ - better `tmux` correlation when the pane option is available
211
+ - child and subagent visibility when OpenCode emits parent session IDs
212
+
213
+ ### In `sse` mode
214
+
215
+ The daemon ignores this plugin and instead connects to OpenCode over its HTTP server.
216
+
217
+ Use `sse` mode when you run:
218
+
219
+ - `opencode --port 4096`
220
+ - `opencode serve`
221
+ - `oc`
222
+
223
+ In `sse` mode:
224
+
225
+ - OpenCode is shown as standalone only
226
+ - OpenCode `tmux` panes are hidden in `scrylog`
227
+ - this plugin is optional and effectively inactive
228
+
229
+ ## Limitations
230
+
231
+ - The plugin can only provide exact `tmux` correlation when it can write `@opencode-session` in the active pane.
232
+ - OpenCode `attach` inside `tmux` still does not have complete exact-session support without an upstream OpenCode signal.
233
+ - If the daemon is down, webhook sends are fire-and-forget and are silently dropped.
234
+
235
+ ## Troubleshooting
236
+
237
+ ### The plugin is installed but OpenCode sessions do not appear
238
+
239
+ Check all of the following:
240
+
241
+ 1. `scrylogd` is running
242
+ 2. `opencode.mode` is `hooks`
243
+ 3. OpenCode config includes `"plugin": ["@scrylog/opencode-plugin"]`
244
+ 4. `SCRYLOG_URL` points to the correct daemon address
245
+ 5. `curl http://127.0.0.1:7788/api/health` succeeds
246
+ 6. `scrylog doctor` reports `OpenCode mode: hooks`
247
+
248
+ ### The daemon says webhook events were skipped
249
+
250
+ Your daemon is probably running with `opencode.mode: "sse"`. Switch it to `hooks` if you want this plugin to drive OpenCode updates.
251
+
252
+ ### I want standalone-only OpenCode monitoring instead
253
+
254
+ Do not use this plugin as the primary integration. Set `opencode.mode` to `sse` in `scrylog` and start OpenCode with a reachable HTTP endpoint or use `oc`.
255
+
256
+ ## Related
257
+
258
+ - [`scrylog`](https://github.com/scrylog/scrylog) for the dashboard, daemon, API, MCP server, and `oc`
259
+ - Root documentation: [`README.md`](https://github.com/scrylog/scrylog#readme)
@@ -0,0 +1 @@
1
+ export { default as server } from "./server";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default as server } from "./server";
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const ScrylogServerPlugin: Plugin;
3
+ export default ScrylogServerPlugin;
package/dist/server.js ADDED
@@ -0,0 +1,35 @@
1
+ import { getTmuxPane, getWebhookUrl, post, setTmuxSessionOption } from "./shared";
2
+ export const ScrylogServerPlugin = async (input) => {
3
+ const { client, directory, $ } = input;
4
+ const webhookUrl = getWebhookUrl();
5
+ if (getTmuxPane()) {
6
+ client.session
7
+ .list()
8
+ .then(({ data }) => {
9
+ if (!data?.length)
10
+ return;
11
+ const latest = [...data]
12
+ .filter((s) => !s.parentID)
13
+ .sort((a, b) => (b.time.updated ?? 0) - (a.time.updated ?? 0))[0];
14
+ if (latest?.id)
15
+ setTmuxSessionOption(latest.id, $);
16
+ })
17
+ .catch(() => { });
18
+ }
19
+ return {
20
+ event: async ({ event }) => {
21
+ post(webhookUrl, {
22
+ event: event.type,
23
+ directory,
24
+ ...event.properties,
25
+ });
26
+ if (event.type === "session.created" || event.type === "session.updated") {
27
+ const sessionID = event.properties.info?.id;
28
+ const parentID = event.properties.info?.parentID;
29
+ if (typeof sessionID === "string" && !parentID)
30
+ setTmuxSessionOption(sessionID, $);
31
+ }
32
+ },
33
+ };
34
+ };
35
+ export default ScrylogServerPlugin;
@@ -0,0 +1,6 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export declare const DEFAULT_WEBHOOK_URL = "http://127.0.0.1:7788/api/webhook/opencode";
3
+ export declare function getWebhookUrl(): string;
4
+ export declare function post(webhookUrl: string, body: Record<string, unknown>): void;
5
+ export declare function getTmuxPane(): string | undefined;
6
+ export declare function setTmuxSessionOption(sessionID: string, $?: PluginInput["$"]): void;
package/dist/shared.js ADDED
@@ -0,0 +1,31 @@
1
+ export const DEFAULT_WEBHOOK_URL = "http://127.0.0.1:7788/api/webhook/opencode";
2
+ export function getWebhookUrl() {
3
+ return process.env["SCRYLOG_URL"] ?? DEFAULT_WEBHOOK_URL;
4
+ }
5
+ export function post(webhookUrl, body) {
6
+ fetch(webhookUrl, {
7
+ method: "POST",
8
+ headers: { "Content-Type": "application/json" },
9
+ body: JSON.stringify(body),
10
+ }).catch(() => {
11
+ // Ignore all errors — daemon may not be running
12
+ });
13
+ }
14
+ export function getTmuxPane() {
15
+ const pane = process.env["TMUX_PANE"];
16
+ if (!pane)
17
+ return undefined;
18
+ if (!/^%\d+$/.test(pane))
19
+ return undefined;
20
+ return pane;
21
+ }
22
+ export function setTmuxSessionOption(sessionID, $) {
23
+ const tmuxPane = getTmuxPane();
24
+ if (!tmuxPane || !$)
25
+ return;
26
+ $ `tmux set-option -p -t ${tmuxPane} @opencode-session ${sessionID}`
27
+ .quiet()
28
+ .catch(() => {
29
+ // Ignore errors — tmux may not be installed or pane may be gone
30
+ });
31
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@scrylog/opencode-plugin",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin that pushes session events to scrylog daemon",
5
+ "author": "Joel D. Martinez",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/scrylog/scrylog.git",
14
+ "directory": "packages/opencode-plugin"
15
+ },
16
+ "homepage": "https://github.com/scrylog/scrylog/tree/main/packages/opencode-plugin#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/scrylog/scrylog/issues"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/index.js",
23
+ "types": "./dist/index.d.ts"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "check-types": "tsc --noEmit",
34
+ "clean": "rm -rf .turbo && rm -rf dist",
35
+ "clean:node-modules": "rm -rf node_modules",
36
+ "dev": "tsc --watch",
37
+ "prepack": "npm run build",
38
+ "pack:dry-run": "npm pack --dry-run"
39
+ },
40
+ "keywords": [
41
+ "opencode",
42
+ "plugin",
43
+ "scrylog",
44
+ "hooks",
45
+ "webhook"
46
+ ],
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "devDependencies": {
51
+ "@opencode-ai/plugin": "1.4.0",
52
+ "@opencode-ai/sdk": "1.4.10",
53
+ "@types/node": "catalog:",
54
+ "typescript": "catalog:"
55
+ },
56
+ "peerDependencies": {
57
+ "@opencode-ai/sdk": "*"
58
+ }
59
+ }