opencode-osc9 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/README.md +36 -0
- package/dist/index.js +53 -0
- package/package.json +30 -0
- package/try.ts +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<div align="right">
|
|
2
|
+
|
|
3
|
+
<img src="./notification.png" width="400" />
|
|
4
|
+
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
# opencode-osc9
|
|
8
|
+
|
|
9
|
+
A zero-dependency [opencode](https://opencode.ai) plugin that sends desktop notifications via [**OSC 9**](https://ghostty.org/docs/vt/osc/9) escape sequences when tasks complete.
|
|
10
|
+
|
|
11
|
+
Works with any terminal that supports OSC 9, including **Ghostty**, **iTerm2**, **kitty**, **WezTerm**, and others.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
Add `"opencode-osc9"` to the `plugins` array in your opencode config (usually `~/.config/opencode/opencode.jsonc`):
|
|
16
|
+
|
|
17
|
+
```jsonc
|
|
18
|
+
{
|
|
19
|
+
"plugin": ["opencode-osc9@latest"]
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Try it
|
|
24
|
+
|
|
25
|
+
You can test OSC 9 support in your terminal by running the included `try.ts`:
|
|
26
|
+
|
|
27
|
+
> [!TIP]
|
|
28
|
+
> Most terminals won't fire a notification if the window is focused so a 3 second delay is baked into the `try.ts` script to give you time to change app focus.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
# bun
|
|
32
|
+
bun try.ts
|
|
33
|
+
|
|
34
|
+
# node (v24+)
|
|
35
|
+
node try.ts
|
|
36
|
+
```
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// index.ts
|
|
2
|
+
import { closeSync, openSync, writeSync } from "fs";
|
|
3
|
+
function osc9(text) {
|
|
4
|
+
const safe = text.replace(/[\x00-\x1f\x7f]/g, " ").slice(0, 256);
|
|
5
|
+
return `\x1B]9;${safe}\x1B\\`;
|
|
6
|
+
}
|
|
7
|
+
async function notify(text, log) {
|
|
8
|
+
let fd;
|
|
9
|
+
try {
|
|
10
|
+
fd = openSync("/dev/tty", "w");
|
|
11
|
+
writeSync(fd, osc9(text));
|
|
12
|
+
} catch (err) {
|
|
13
|
+
await log({
|
|
14
|
+
body: {
|
|
15
|
+
service: "opencode-osc9",
|
|
16
|
+
level: "warn",
|
|
17
|
+
message: "Failed to notify",
|
|
18
|
+
extra: { text, err: err instanceof Error ? err.message : String(err) }
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
} finally {
|
|
22
|
+
if (fd !== undefined) {
|
|
23
|
+
try {
|
|
24
|
+
closeSync(fd);
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
var OpencodeOsc9 = async ({ client }) => {
|
|
30
|
+
return {
|
|
31
|
+
event: async ({ event }) => {
|
|
32
|
+
const { type, properties } = event;
|
|
33
|
+
switch (type) {
|
|
34
|
+
case "session.idle":
|
|
35
|
+
await notify("session idle", client.app.log);
|
|
36
|
+
break;
|
|
37
|
+
case "permission.ask": {
|
|
38
|
+
const perm = properties?.permission ?? "unknown";
|
|
39
|
+
await notify(`needs approval: ${perm}`, client.app.log);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "session.error": {
|
|
43
|
+
const err = properties?.error;
|
|
44
|
+
await notify(`error${err?.message ? ": " + err.message : ""}`, client.app.log);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export {
|
|
52
|
+
OpencodeOsc9
|
|
53
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-osc9",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "opencode plugin to send system notification via OSC 9",
|
|
5
|
+
"author": "tbeseda",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"try.ts"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "bun build index.ts --outdir dist --target node",
|
|
15
|
+
"prepublishOnly": "bun run build",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "bun test"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@opencode-ai/plugin": "^1.14.49",
|
|
21
|
+
"@types/bun": "^1.3.14",
|
|
22
|
+
"typescript": "^6"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"opencode",
|
|
26
|
+
"opencode-plugin",
|
|
27
|
+
"notifications",
|
|
28
|
+
"alerts"
|
|
29
|
+
]
|
|
30
|
+
}
|
package/try.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { setTimeout } from "timers/promises"
|
|
3
|
+
import { OpencodeOsc9 } from "./index.ts"
|
|
4
|
+
|
|
5
|
+
const client = await OpencodeOsc9({
|
|
6
|
+
client: {
|
|
7
|
+
app: {
|
|
8
|
+
log(payload: any) {
|
|
9
|
+
console.error("Unable to send notification via OSC 9")
|
|
10
|
+
console.log(JSON.stringify(payload, null, 2))
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
console.log("Unfocus your terminal and wait 3 seconds")
|
|
17
|
+
await setTimeout(3000)
|
|
18
|
+
await client.event({
|
|
19
|
+
event: {
|
|
20
|
+
type: "session.error",
|
|
21
|
+
properties: {
|
|
22
|
+
error: { message: "agent ran rm -rf /" },
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
console.log("Error event sent")
|
|
27
|
+
console.log("You should have seen a notification")
|