@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 +21 -0
- package/README.md +259 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +35 -0
- package/dist/shared.d.ts +6 -0
- package/dist/shared.js +31 -0
- package/package.json +59 -0
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)
|
package/dist/index.d.ts
ADDED
|
@@ -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";
|
package/dist/server.d.ts
ADDED
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;
|
package/dist/shared.d.ts
ADDED
|
@@ -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
|
+
}
|