@jack-yang/opencode-bark-notify 1.0.2 → 1.1.1
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 +7 -16
- package/README.zh-CN.md +6 -8
- package/index.ts +31 -35
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# opencode-bark-notify
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[Bark](https://github.com/Finb/Bark) push notifications for [OpenCode](https://opencode.ai).
|
|
4
4
|
|
|
5
|
-
Get **detailed agent messages** on your iPhone
|
|
5
|
+
Get **detailed agent messages** on your iPhone: task finished, permission needed, or a question waiting for you.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **Focus-aware** — Suppresses notifications when your terminal is the frontmost app
|
|
10
9
|
- **Detailed content** — Sends the agent's actual last message, not just "task completed"
|
|
11
10
|
- **Category badges** — iOS subtitle shows what happened: ✅ Responded / 🔐 Wait for Permission / ❓ Input Needed
|
|
12
11
|
- **Session-aware** — Includes the session title in every notification
|
|
@@ -59,7 +58,6 @@ Restart OpenCode.
|
|
|
59
58
|
| `barkUrl` | string | `https://api.day.app` | Bark server URL (for self-hosted) |
|
|
60
59
|
| `level` | string | `timeSensitive` | iOS notification level |
|
|
61
60
|
| `group` | string | `opencode` | iOS notification group |
|
|
62
|
-
| `terminals` | string[] | `["Ghostty", "Terminal", "iTerm2", ...]` | Terminal app names for focus detection |
|
|
63
61
|
|
|
64
62
|
### Example with all options
|
|
65
63
|
|
|
@@ -70,24 +68,17 @@ Restart OpenCode.
|
|
|
70
68
|
"barkKey": "YOUR_KEY",
|
|
71
69
|
"barkUrl": "https://your-bark-server.com",
|
|
72
70
|
"level": "active",
|
|
73
|
-
"group": "my-agent"
|
|
74
|
-
"terminals": ["Ghostty", "WezTerm"]
|
|
71
|
+
"group": "my-agent"
|
|
75
72
|
}]
|
|
76
73
|
]
|
|
77
74
|
}
|
|
78
75
|
```
|
|
79
76
|
|
|
80
|
-
## How focus detection works
|
|
81
|
-
|
|
82
|
-
On macOS, the plugin uses `osascript` to check if a terminal app is the frontmost application. If it is, notifications are suppressed — you're already looking at the terminal. If you've switched to another app or stepped away, notifications fire.
|
|
83
|
-
|
|
84
|
-
On non-macOS, focus detection is disabled (notifications always fire).
|
|
85
|
-
|
|
86
77
|
## Notification types
|
|
87
78
|
|
|
88
79
|
| Event | Badge | Body |
|
|
89
80
|
|-------|-------|------|
|
|
90
|
-
| Session complete | **✅ Responded** |
|
|
81
|
+
| Session complete | **✅ Responded** | The agent's last reply (up to 500 chars) |
|
|
91
82
|
| Permission request | **🔐 Wait for Permission** | Permission type + patterns |
|
|
92
83
|
| Question | **❓ Input Needed** | The question text |
|
|
93
84
|
|
|
@@ -95,10 +86,10 @@ On non-macOS, focus detection is disabled (notifications always fire).
|
|
|
95
86
|
|
|
96
87
|
This plugin complements [@mohak34/opencode-notifier](https://github.com/mohak34/opencode-notifier) (desktop notifications). Use both together:
|
|
97
88
|
|
|
98
|
-
- **Desktop** (osascript/sound/bell) →
|
|
99
|
-
- **Bark** (iPhone push) →
|
|
89
|
+
- **Desktop** (osascript/sound/bell) → on the computer
|
|
90
|
+
- **Bark** (iPhone push) → on your phone
|
|
100
91
|
|
|
101
|
-
|
|
92
|
+
This plugin always sends; if you want desktop notifications only when the terminal is in the background, set `suppressWhenFocused` in opencode-notifier.
|
|
102
93
|
|
|
103
94
|
## License
|
|
104
95
|
|
package/README.zh-CN.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# opencode-bark-notify
|
|
2
2
|
|
|
3
|
-
为 [OpenCode](https://opencode.ai) 提供基于 [Bark](https://github.com/Finb/Bark)
|
|
3
|
+
为 [OpenCode](https://opencode.ai) 提供基于 [Bark](https://github.com/Finb/Bark) 的手机推送通知。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
任务完成、需要授权、等你回答时,iPhone 上收到 **agent 的实际消息内容**。
|
|
6
6
|
|
|
7
7
|
## 功能
|
|
8
8
|
|
|
9
|
-
- **焦点感知** — 终端在前台时不发通知,切走或离开时才推
|
|
10
9
|
- **详细内容** — 推送 agent 的最后一条消息,不只是"任务完成"
|
|
11
10
|
- **分类标识** — iOS 副标题显示事件类型:✅ Responded / 🔐 Wait for Permission / ❓ Input Needed
|
|
12
11
|
- **Session 感知** — 每条通知包含 session 标题
|
|
@@ -59,13 +58,12 @@ export BARK_KEY="你的BARK设备KEY"
|
|
|
59
58
|
| `barkUrl` | string | `https://api.day.app` | Bark 服务器地址(自建服务器用) |
|
|
60
59
|
| `level` | string | `timeSensitive` | iOS 通知级别 |
|
|
61
60
|
| `group` | string | `opencode` | iOS 通知分组 |
|
|
62
|
-
| `terminals` | string[] | `["Ghostty", "Terminal", "iTerm2", ...]` | 焦点检测的终端 App 名称 |
|
|
63
61
|
|
|
64
62
|
## 通知类型
|
|
65
63
|
|
|
66
64
|
| 事件 | 标识 | 内容 |
|
|
67
65
|
|------|------|------|
|
|
68
|
-
| Session 完成 | **✅ Responded** | agent
|
|
66
|
+
| Session 完成 | **✅ Responded** | agent 最后一条回复(≤500字) |
|
|
69
67
|
| 需要权限 | **🔐 Wait for Permission** | 权限类型 + 匹配模式 |
|
|
70
68
|
| 需要回答 | **❓ Input Needed** | 问题内容 |
|
|
71
69
|
|
|
@@ -73,10 +71,10 @@ export BARK_KEY="你的BARK设备KEY"
|
|
|
73
71
|
|
|
74
72
|
本插件与 [@mohak34/opencode-notifier](https://github.com/mohak34/opencode-notifier)(桌面通知)互补:
|
|
75
73
|
|
|
76
|
-
- **桌面通知**(声音/弹窗)→
|
|
77
|
-
- **Bark 推送**(手机)→
|
|
74
|
+
- **桌面通知**(声音/弹窗)→ 电脑上
|
|
75
|
+
- **Bark 推送**(手机)→ 手机上
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
本插件总是推送;如果希望桌面通知只在终端不在前台时弹,在 opencode-notifier 里设置 `suppressWhenFocused`。
|
|
80
78
|
|
|
81
79
|
## 许可证
|
|
82
80
|
|
package/index.ts
CHANGED
|
@@ -5,27 +5,11 @@ interface BarkConfig {
|
|
|
5
5
|
barkUrl?: string
|
|
6
6
|
level?: string
|
|
7
7
|
group?: string
|
|
8
|
-
terminals?: string[]
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
const DEFAULT_TERMINALS = ["Ghostty", "Terminal", "iTerm2", "Alacritty", "kitty", "WezTerm"]
|
|
12
10
|
const DEFAULT_BARK_URL = "https://api.day.app"
|
|
13
11
|
const MAX_BODY_LENGTH = 500
|
|
14
12
|
|
|
15
|
-
const isFocused = async (terminals: string[]): Promise<boolean> => {
|
|
16
|
-
if (process.platform !== "darwin") return false
|
|
17
|
-
try {
|
|
18
|
-
const proc = Bun.spawn(
|
|
19
|
-
["osascript", "-e", 'tell application "System Events" to name of first application whose frontmost is true'],
|
|
20
|
-
{ stdout: "pipe", stderr: "pipe" },
|
|
21
|
-
)
|
|
22
|
-
const output = await new Response(proc.stdout).text()
|
|
23
|
-
return terminals.includes(output.trim())
|
|
24
|
-
} catch {
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
13
|
const sendBark = async (
|
|
30
14
|
barkUrl: string,
|
|
31
15
|
barkKey: string,
|
|
@@ -49,7 +33,12 @@ const truncate = (text: string, max: number): string => {
|
|
|
49
33
|
return clean.length > max ? clean.slice(0, max) + "..." : clean
|
|
50
34
|
}
|
|
51
35
|
|
|
52
|
-
|
|
36
|
+
// Every notification is sent, whether or not the terminal is in front. Focus detection
|
|
37
|
+
// (osascript) proved fragile across macOS releases and silently dropped notifications.
|
|
38
|
+
//
|
|
39
|
+
// Only the plugin itself may be exported: opencode calls every exported function of a
|
|
40
|
+
// plugin module as a plugin, so an exported helper would be invoked with the plugin context.
|
|
41
|
+
export const BarkNotifyPlugin: Plugin = async ({ project, client, directory }, config: BarkConfig = {}) => {
|
|
53
42
|
const barkKey = config.barkKey || process.env.BARK_KEY
|
|
54
43
|
if (!barkKey) {
|
|
55
44
|
console.warn(
|
|
@@ -61,54 +50,61 @@ export const BarkNotifyPlugin: Plugin = async ({ project, client }, config: Bark
|
|
|
61
50
|
const barkUrl = config.barkUrl || process.env.BARK_URL || DEFAULT_BARK_URL
|
|
62
51
|
const level = config.level || "timeSensitive"
|
|
63
52
|
const group = config.group || "opencode"
|
|
64
|
-
const terminals = config.terminals || DEFAULT_TERMINALS
|
|
65
53
|
|
|
54
|
+
// project.id is "global" outside a git repository; the folder name says more.
|
|
55
|
+
const folder = String(directory || (project as any)?.worktree || "").split("/").filter(Boolean).pop()
|
|
56
|
+
let currentTitle = `OpenCode · ${folder || "Session"}`
|
|
57
|
+
|
|
58
|
+
// Only assistant text is worth sending; user prompts arrive as text parts too.
|
|
59
|
+
const assistantMessages = new Set<string>()
|
|
66
60
|
let lastMessage = ""
|
|
67
|
-
let currentTitle = `OpenCode · ${project?.id || "Session"}`
|
|
68
61
|
|
|
69
62
|
const send = (subtitle: string, body: string) =>
|
|
70
63
|
sendBark(barkUrl, barkKey, currentTitle, subtitle, body, level, group)
|
|
71
64
|
|
|
72
65
|
return {
|
|
73
|
-
event: async ({ event }) => {
|
|
66
|
+
event: async ({ event }: { event: any }) => {
|
|
67
|
+
if (event.type === "message.updated") {
|
|
68
|
+
const info = event.properties?.info
|
|
69
|
+
if (info?.role === "assistant" && info.id) assistantMessages.add(info.id)
|
|
70
|
+
}
|
|
71
|
+
|
|
74
72
|
if (event.type === "message.part.updated") {
|
|
75
73
|
const part = event.properties?.part
|
|
76
|
-
if (part?.type === "text" && part.text) {
|
|
74
|
+
if (part?.type === "text" && part.text && !part.synthetic && assistantMessages.has(part.messageID)) {
|
|
77
75
|
lastMessage = part.text
|
|
78
76
|
}
|
|
79
77
|
if (part?.type === "tool" && part.tool === "question" && part.state?.status === "running") {
|
|
80
|
-
if (await isFocused(terminals)) return
|
|
81
78
|
const questions = part.state?.input?.questions || []
|
|
82
79
|
const q = questions[0]?.question || "Input needed"
|
|
83
80
|
await send("❓ Input Needed", q)
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
// Permission requests arrive as an event; the old "permission.ask" hook is gone in opencode 1.19+.
|
|
85
|
+
if (event.type === "permission.asked") {
|
|
86
|
+
const p = event.properties || {}
|
|
87
|
+
const patterns: string[] = p.patterns || []
|
|
88
|
+
let body = p.permission || "permission"
|
|
89
|
+
if (patterns.length) body += `: ${patterns.join(", ")}`
|
|
90
|
+
await send("🔐 Wait for Permission", truncate(body, MAX_BODY_LENGTH))
|
|
91
|
+
}
|
|
89
92
|
|
|
93
|
+
if (event.type === "session.idle") {
|
|
90
94
|
const sid = event.properties?.sessionID
|
|
91
95
|
if (sid) {
|
|
92
96
|
try {
|
|
93
|
-
const sessions = await client.session.list({ limit: 50 })
|
|
94
|
-
const s = sessions.data?.find((s: any) => s.id === sid)
|
|
97
|
+
const sessions = await client.session.list({ limit: 50 } as any)
|
|
98
|
+
const s = (sessions as any).data?.find((s: any) => s.id === sid)
|
|
95
99
|
if (s?.title) currentTitle = `OpenCode · ${s.title}`
|
|
96
100
|
} catch {}
|
|
97
101
|
}
|
|
98
102
|
|
|
99
103
|
const body = lastMessage ? truncate(lastMessage, MAX_BODY_LENGTH) : "Task completed"
|
|
104
|
+
lastMessage = ""
|
|
100
105
|
await send("✅ Responded", body)
|
|
101
106
|
}
|
|
102
107
|
},
|
|
103
|
-
|
|
104
|
-
"permission.ask": async (input: any) => {
|
|
105
|
-
if (await isFocused(terminals)) return
|
|
106
|
-
const permType = input?.type || "permission"
|
|
107
|
-
const patterns = input?.patterns || []
|
|
108
|
-
let body = permType
|
|
109
|
-
if (patterns.length) body += `: ${patterns.join(", ")}`
|
|
110
|
-
await send("🔐 Wait for Permission", body)
|
|
111
|
-
},
|
|
112
108
|
}
|
|
113
109
|
}
|
|
114
110
|
|
package/package.json
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@jack-yang/opencode-bark-notify",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "1.1.1",
|
|
5
|
+
"description": "Bark push notifications for OpenCode — get the agent's actual messages on your iPhone",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"main": "./index.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
10
|
"import": "./index.ts"
|
|
11
|
+
},
|
|
12
|
+
"./server": {
|
|
13
|
+
"import": "./index.ts"
|
|
10
14
|
}
|
|
11
15
|
},
|
|
12
16
|
"engines": {
|
|
13
17
|
"opencode": ">=1.4.3"
|
|
14
18
|
},
|
|
15
|
-
"peerDependencies": {
|
|
16
|
-
"@opencode-ai/plugin": ">=1.4.3"
|
|
17
|
-
},
|
|
18
19
|
"devDependencies": {
|
|
19
|
-
"
|
|
20
|
-
"typescript": "^5.9.2"
|
|
20
|
+
"@opencode-ai/plugin": "^1.19.5",
|
|
21
|
+
"typescript": "^5.9.2",
|
|
22
|
+
"vitest": "^5.1.0"
|
|
21
23
|
},
|
|
22
24
|
"scripts": {
|
|
23
25
|
"test": "vitest run",
|
|
@@ -33,8 +35,7 @@
|
|
|
33
35
|
"bark",
|
|
34
36
|
"notification",
|
|
35
37
|
"push-notification",
|
|
36
|
-
"ios"
|
|
37
|
-
"focus-aware"
|
|
38
|
+
"ios"
|
|
38
39
|
],
|
|
39
40
|
"license": "MIT",
|
|
40
41
|
"repository": {
|