opencode-termux-notify 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/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/assets/audio/bip-bop-01.mp3 +0 -0
- package/assets/audio/bip-bop-03.mp3 +0 -0
- package/assets/audio/nope-03.mp3 +0 -0
- package/assets/audio/staplebops-06.mp3 +0 -0
- package/assets/audio/yup-01.mp3 +0 -0
- package/dist/audio.d.ts +4 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +55 -0
- package/dist/audio.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +17 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +40 -0
- package/dist/constants.js.map +1 -0
- package/dist/dedup.d.ts +3 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/dedup.js +57 -0
- package/dist/dedup.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +4 -0
- package/dist/env.js.map +1 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +52 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/notify.d.ts +3 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +23 -0
- package/dist/notify.js.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/v1.d.ts +21 -0
- package/dist/v1.d.ts.map +1 -0
- package/dist/v1.js +133 -0
- package/dist/v1.js.map +1 -0
- package/index.js +4 -0
- package/package.json +80 -0
- package/src/audio.ts +50 -0
- package/src/config.ts +50 -0
- package/src/constants.ts +43 -0
- package/src/dedup.ts +58 -0
- package/src/env.ts +3 -0
- package/src/events.ts +58 -0
- package/src/index.ts +127 -0
- package/src/notify.ts +25 -0
- package/src/types.ts +39 -0
- package/src/v1.ts +140 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-09-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial npm release, extracted from `~/.config/opencode/plugins/termux-notify`
|
|
12
|
+
- `Plugin.define` entrypoint using `@opencode-ai/plugin` (`^1.18.27`, `v2/promise`)
|
|
13
|
+
- Cross-instance deduplication via shared JSON file + stable `termux-notification --id`
|
|
14
|
+
- Per-session cooldown (5s), global throttle (1s), `event.id` TTL (60s) + `active`/`errored` tracking (mirrors `packages/tui/src/feature-plugins/system/notifications.ts:59-86`)
|
|
15
|
+
- Session-aware titles via `ctx.session.get` + `parentID` check for `subagent_done`
|
|
16
|
+
- 6 wired attention sounds (from `packages/tui/src/attention.ts:19-60` + `packages/ui/src/assets/audio/`):
|
|
17
|
+
- `default` → `bip-bop-01.mp3` (400,200,400)
|
|
18
|
+
- `question` → `bip-bop-03.mp3` (500,250,500) on `question.asked`/`v2`
|
|
19
|
+
- `permission` → `staplebops-06.mp3` (600,200,600) on `permission.asked`/`v2`
|
|
20
|
+
- `error` → `nope-03.mp3` (800,300,800,300,800)
|
|
21
|
+
- `done` → `bip-bop-01.mp3` (400,200,400)
|
|
22
|
+
- `subagent_done` → `yup-01.mp3` (200,100,200) — obeys `notifySubagents` (default true, `false` = sound-only like TUI)
|
|
23
|
+
- Bundled mp3 assets at `assets/audio/*.mp3` + playback via `termux-media-player play` (`termux-notification --sound` is boolean only — see `termux-notification -h`)
|
|
24
|
+
- Configurable via `opencode.json` `options`: `bin`, `mediaBin`, `sharedPath`, `seenTTL`, `sessionCooldown`, `globalCooldown`, `sound`, `playSound`, `vibrate`, `priority`, `requireTermux`, `kinds` (6), `notifySubagents`, `title_<kind>`/`content_<kind>`
|
|
25
|
+
- TypeScript source (`src/index.ts`) + runtime JS (`src/index.js`) + types (`src/index.d.ts`)
|
|
26
|
+
- Professional package scaffolding: README, LICENSE (MIT), CHANGELOG, `.gitignore`, `tsconfig.json`
|
|
27
|
+
|
|
28
|
+
[0.1.0]: https://github.com/Victozee26/opencode-termux-notify/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 opencode-termux-notify contributors
|
|
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,132 @@
|
|
|
1
|
+
# opencode-termux-notify
|
|
2
|
+
|
|
3
|
+
Termux notification plugin for [OpenCode](https://opencode.ai) on Android. Routes OpenCode attention events to `termux-notification` and `termux-media-player`. **Default is V1 (`opencode`), V2 (`opencode2`) via `opencode-termux-notify/v2`.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/opencode-termux-notify)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 6 notification kinds (default, question, permission, error, done, subagent_done) with distinct vibration and audio via `termux-media-player` from Opencode
|
|
11
|
+
- Cross-instance deduplication via shared JSON file and stable notification IDs
|
|
12
|
+
- Spam prevention via event TTL (60s), per-session cooldown (5s), and global throttle (1s)
|
|
13
|
+
- Session-aware titles resolved from OpenCode session metadata
|
|
14
|
+
- Fully configurable: binaries, vibration, priority, cooldowns, kinds, and subagent handling
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- Android + Termux with `termux-api` (`pkg install termux-api` and install Termux:API app)
|
|
19
|
+
- Node >= 20, OpenCode >= 1.18.27
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
**V1 (default) — `opencode` (1.18.27+):**
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
opencode plugin opencode-termux-notify # latest
|
|
27
|
+
opencode plugin opencode-termux-notify -g # install globally instead of per-project
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add to `opencode.json`:
|
|
31
|
+
|
|
32
|
+
```jsonc
|
|
33
|
+
// ~/.config/opencode/opencode.json (V1: singular "plugin")
|
|
34
|
+
{
|
|
35
|
+
"$schema": "https://opencode.ai/config.json",
|
|
36
|
+
"plugin": ["opencode-termux-notify"]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**V2 — `opencode2` (beta):**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
opencode2 plugin add opencode-termux-notify/v2
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Add to `opencode.jsonc` (`opencode.json(c)`):
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
// ~/.config/opencode/opencode.jsonc (V2: plural "plugins")
|
|
50
|
+
{
|
|
51
|
+
"$schema": "https://opencode.ai/config.json",
|
|
52
|
+
"plugins": ["opencode-termux-notify/v2"]
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Local path (this repo) for V2 — must be a directory (`opencode.jsonc`):
|
|
57
|
+
|
|
58
|
+
```jsonc
|
|
59
|
+
// opencode.jsonc
|
|
60
|
+
{
|
|
61
|
+
"plugins": ["/data/data/com.termux/files/home/opencode-termux-notify/v2"]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
With options (same for V1/V2, note `plugin` vs `plugins` and `opencode.json` vs `opencode.jsonc`):
|
|
68
|
+
|
|
69
|
+
```jsonc
|
|
70
|
+
// V1 — opencode.json
|
|
71
|
+
{
|
|
72
|
+
"plugin": [["opencode-termux-notify", {
|
|
73
|
+
"kinds": ["question", "permission", "error", "done", "subagent_done"],
|
|
74
|
+
"notifySubagents": true,
|
|
75
|
+
"priority": "high"
|
|
76
|
+
}]]
|
|
77
|
+
}
|
|
78
|
+
// V2 — opencode.jsonc
|
|
79
|
+
{
|
|
80
|
+
"plugins": [{
|
|
81
|
+
"package": "opencode-termux-notify/v2",
|
|
82
|
+
"options": {
|
|
83
|
+
"kinds": ["question", "permission", "error", "done", "subagent_done"],
|
|
84
|
+
"notifySubagents": true,
|
|
85
|
+
"priority": "high"
|
|
86
|
+
}
|
|
87
|
+
}]
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Restart the opencode after editing config:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
opencode2 service restart
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Grant notification permission on Android 13+ under Settings > Apps > Termux > Notifications. Verify with:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
termux-notification --title test --content test --priority high --vibrate "400,200,400" --sound --id test
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
| Option | Type | Default | Description |
|
|
106
|
+
|---|---|---|---|
|
|
107
|
+
| `bin` | `string` | `/data/data/com.termux/files/usr/bin/termux-notification` | Notification binary |
|
|
108
|
+
| `mediaBin` | `string` | `/data/data/com.termux/files/usr/bin/termux-media-player` | Audio player binary |
|
|
109
|
+
| `sharedPath` | `string` | `os.tmpdir()/termux-notify-shared.json` | Deduplication file |
|
|
110
|
+
| `seenTTL` | `number` | `60000` | Event ID TTL (ms) |
|
|
111
|
+
| `sessionCooldown` | `number` | `5000` | Per-session debounce (ms) |
|
|
112
|
+
| `globalCooldown` | `number` | `1000` | Global throttle (ms) |
|
|
113
|
+
| `priority` | `string` | per-kind | Notification priority |
|
|
114
|
+
| `sound` | `boolean` | `true` | Enable sound |
|
|
115
|
+
| `playSound` | `boolean` | `true` | Play bundled mp3 |
|
|
116
|
+
| `vibrate` | `boolean` | `true` | Enable vibration |
|
|
117
|
+
| `requireTermux` | `boolean` | `true` | Warn if not in Termux |
|
|
118
|
+
| `kinds` | `string[]` | all kinds | Enabled event kinds |
|
|
119
|
+
| `notifySubagents` | `boolean` | `true` | Notify for subagents (false = sound only) |
|
|
120
|
+
| `title_<kind>` / `content_<kind>` | `string` | per-kind | Title/content override |
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm install
|
|
126
|
+
npm run typecheck
|
|
127
|
+
npm run build
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
[MIT](./LICENSE)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBpF"}
|
package/dist/audio.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { SOUND_FILES } from "./constants.js";
|
|
6
|
+
export function resolveAudioPath(file) {
|
|
7
|
+
try {
|
|
8
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const candidates = [
|
|
10
|
+
join(here, "../assets/audio", file),
|
|
11
|
+
join(here, "assets/audio", file),
|
|
12
|
+
join(here, "../../assets/audio", file),
|
|
13
|
+
join(here, "../src/assets/audio", file),
|
|
14
|
+
join(process.cwd(), "assets/audio", file),
|
|
15
|
+
join(process.cwd(), "src/assets/audio", file),
|
|
16
|
+
];
|
|
17
|
+
for (const p of candidates)
|
|
18
|
+
if (existsSync(p))
|
|
19
|
+
return p;
|
|
20
|
+
return candidates[0];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return file;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function playAudio(soundName, opts) {
|
|
27
|
+
if (!opts.playSound)
|
|
28
|
+
return;
|
|
29
|
+
const file = SOUND_FILES[soundName];
|
|
30
|
+
if (!file)
|
|
31
|
+
return;
|
|
32
|
+
const path = resolveAudioPath(file);
|
|
33
|
+
if (!existsSync(path)) {
|
|
34
|
+
console.warn(`[termux-notify] audio file not found: ${path} for ${soundName}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const bin = existsSync(opts.mediaBin) ? opts.mediaBin : "termux-media-player";
|
|
38
|
+
await new Promise((resolve) => {
|
|
39
|
+
const p = spawn(bin, ["play", path], { stdio: "ignore" });
|
|
40
|
+
p.on("error", (err) => {
|
|
41
|
+
console.error(`[termux-notify] media play failed for ${soundName}:`, err.message);
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
p.on("close", () => resolve());
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
try {
|
|
47
|
+
;
|
|
48
|
+
p.unref?.();
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
resolve();
|
|
52
|
+
}, 3000);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=audio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAG5C,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QACpD,MAAM,UAAU,GAAG;YACjB,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,CAAC;YACnC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC;YAChC,IAAI,CAAC,IAAI,EAAE,oBAAoB,EAAE,IAAI,CAAC;YACtC,IAAI,CAAC,IAAI,EAAE,qBAAqB,EAAE,IAAI,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,kBAAkB,EAAE,IAAI,CAAC;SAC9C,CAAA;QACD,KAAK,MAAM,CAAC,IAAI,UAAU;YAAE,IAAI,UAAU,CAAC,CAAC,CAAC;gBAAE,OAAO,CAAC,CAAA;QACvD,OAAO,UAAU,CAAC,CAAC,CAAE,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAiB,EAAE,IAAkB;IACnE,IAAI,CAAC,IAAI,CAAC,SAAS;QAAE,OAAM;IAC3B,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,CAAA;IACnC,IAAI,CAAC,IAAI;QAAE,OAAM;IACjB,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,yCAAyC,IAAI,QAAQ,SAAS,EAAE,CAAC,CAAA;QAC9E,OAAM;IACR,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAA;IAC7E,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;QACzD,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACpB,OAAO,CAAC,KAAK,CAAC,yCAAyC,SAAS,GAAG,EAAG,GAAa,CAAC,OAAO,CAAC,CAAA;YAC5F,OAAO,EAAE,CAAA;QACX,CAAC,CAAC,CAAA;QACF,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;QAC9B,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC;gBACH,CAAC;gBAAC,CAAS,CAAC,KAAK,EAAE,EAAE,CAAA;YACvB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,OAAO,EAAE,CAAA;QACX,CAAC,EAAE,IAAI,CAAC,CAAA;IACV,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ResolvedOpts, TermuxNotifyOptions } from "./types.js";
|
|
2
|
+
export declare function resolveOpts(userOpts: TermuxNotifyOptions): ResolvedOpts;
|
|
3
|
+
export declare function getEnabledKinds(userOpts: TermuxNotifyOptions): Set<string>;
|
|
4
|
+
export declare function getTitle(kind: string, sessionName: string, userOpts: TermuxNotifyOptions): string;
|
|
5
|
+
export declare function getContent(kind: string, userOpts: TermuxNotifyOptions): string;
|
|
6
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEnE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,mBAAmB,GAAG,YAAY,CAMvE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,mBAAmB,GAAG,GAAG,CAAC,MAAM,CAAC,CAY1E;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAWjG;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAW9E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { DEFAULTS } from "./constants.js";
|
|
2
|
+
export function resolveOpts(userOpts) {
|
|
3
|
+
const opts = { ...DEFAULTS, ...userOpts };
|
|
4
|
+
if (typeof userOpts.sharedPath === "string" && userOpts.sharedPath.trim())
|
|
5
|
+
opts.sharedPath = userOpts.sharedPath;
|
|
6
|
+
if (typeof userOpts.bin === "string" && userOpts.bin.trim())
|
|
7
|
+
opts.bin = userOpts.bin;
|
|
8
|
+
if (typeof userOpts.mediaBin === "string" && userOpts.mediaBin.trim())
|
|
9
|
+
opts.mediaBin = userOpts.mediaBin;
|
|
10
|
+
return opts;
|
|
11
|
+
}
|
|
12
|
+
export function getEnabledKinds(userOpts) {
|
|
13
|
+
const enabledKinds = new Set(Array.isArray(userOpts.kinds) && userOpts.kinds.length
|
|
14
|
+
? userOpts.kinds
|
|
15
|
+
: ["default", "question", "permission", "error", "done", "subagent_done"]);
|
|
16
|
+
if (enabledKinds.has("idle")) {
|
|
17
|
+
enabledKinds.delete("idle");
|
|
18
|
+
enabledKinds.add("done");
|
|
19
|
+
enabledKinds.add("default");
|
|
20
|
+
}
|
|
21
|
+
return enabledKinds;
|
|
22
|
+
}
|
|
23
|
+
export function getTitle(kind, sessionName, userOpts) {
|
|
24
|
+
const titles = {
|
|
25
|
+
default: `OpenCode — ${sessionName}`,
|
|
26
|
+
done: `OpenCode — ${sessionName}`,
|
|
27
|
+
subagent_done: `OpenCode (subagent) — ${sessionName}`,
|
|
28
|
+
question: `OpenCode question — ${sessionName}`,
|
|
29
|
+
permission: `OpenCode permission — ${sessionName}`,
|
|
30
|
+
error: `OpenCode error — ${sessionName}`,
|
|
31
|
+
};
|
|
32
|
+
const key = `title_${kind}`;
|
|
33
|
+
return typeof userOpts[key] === "string" ? userOpts[key] : titles[kind] || titles.default;
|
|
34
|
+
}
|
|
35
|
+
export function getContent(kind, userOpts) {
|
|
36
|
+
const contents = {
|
|
37
|
+
default: "Session finished",
|
|
38
|
+
done: "Session done",
|
|
39
|
+
subagent_done: "Subagent done",
|
|
40
|
+
question: "Question needs input",
|
|
41
|
+
permission: "Permission needs input",
|
|
42
|
+
error: "Session errored",
|
|
43
|
+
};
|
|
44
|
+
const key = `content_${kind}`;
|
|
45
|
+
return typeof userOpts[key] === "string" ? userOpts[key] : contents[kind] || contents.default;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAGzC,MAAM,UAAU,WAAW,CAAC,QAA6B;IACvD,MAAM,IAAI,GAAiB,EAAE,GAAG,QAAQ,EAAE,GAAG,QAAQ,EAAkB,CAAA;IACvE,IAAI,OAAO,QAAQ,CAAC,UAAU,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAA;IAChH,IAAI,OAAQ,QAAgB,CAAC,GAAG,KAAK,QAAQ,IAAK,QAAgB,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,GAAG,GAAI,QAAgB,CAAC,GAAG,CAAA;IAC/G,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAA;IACxG,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAA6B;IAC3D,MAAM,YAAY,GAAG,IAAI,GAAG,CAC1B,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM;QACpD,CAAC,CAAE,QAAQ,CAAC,KAAkB;QAC9B,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,CAC5E,CAAA;IACD,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC3B,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACxB,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,YAAY,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,WAAmB,EAAE,QAA6B;IACvF,MAAM,MAAM,GAA2B;QACrC,OAAO,EAAE,cAAc,WAAW,EAAE;QACpC,IAAI,EAAE,cAAc,WAAW,EAAE;QACjC,aAAa,EAAE,yBAAyB,WAAW,EAAE;QACrD,QAAQ,EAAE,uBAAuB,WAAW,EAAE;QAC9C,UAAU,EAAE,yBAAyB,WAAW,EAAE;QAClD,KAAK,EAAE,oBAAoB,WAAW,EAAE;KACzC,CAAA;IACD,MAAM,GAAG,GAAG,SAAS,IAAI,EAA+B,CAAA;IACxD,OAAO,OAAQ,QAAgB,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,QAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,OAAQ,CAAA;AAC9G,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,QAA6B;IACpE,MAAM,QAAQ,GAA2B;QACvC,OAAO,EAAE,kBAAkB;QAC3B,IAAI,EAAE,cAAc;QACpB,aAAa,EAAE,eAAe;QAC9B,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,wBAAwB;QACpC,KAAK,EAAE,iBAAiB;KACzB,CAAA;IACD,MAAM,GAAG,GAAG,WAAW,IAAI,EAA+B,CAAA;IAC1D,OAAO,OAAQ,QAAgB,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,QAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,OAAQ,CAAA;AAClH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const SOUND_FILES: Record<string, string>;
|
|
2
|
+
export declare const VIBRATE_PATTERNS: Record<string, string>;
|
|
3
|
+
export declare const PRIORITY_BY_KIND: Record<string, string>;
|
|
4
|
+
export declare const DEFAULTS: {
|
|
5
|
+
bin: string;
|
|
6
|
+
mediaBin: string;
|
|
7
|
+
sharedPath: string;
|
|
8
|
+
seenTTL: number;
|
|
9
|
+
sessionCooldown: number;
|
|
10
|
+
globalCooldown: number;
|
|
11
|
+
sound: boolean;
|
|
12
|
+
playSound: boolean;
|
|
13
|
+
vibrate: boolean;
|
|
14
|
+
requireTermux: boolean;
|
|
15
|
+
priority: string | undefined;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO9C,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOnD,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOnD,CAAA;AAED,eAAO,MAAM,QAAQ;IACnB,GAAG;IACH,QAAQ;IACR,UAAU;IACV,OAAO;IACP,eAAe;IACf,cAAc;IACd,KAAK;IACL,SAAS;IACT,OAAO;IACP,aAAa;IACb,QAAQ,EAAe,MAAM,GAAG,SAAS;CAC1C,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { tmpdir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const SOUND_FILES = {
|
|
4
|
+
default: "bip-bop-01.mp3",
|
|
5
|
+
question: "bip-bop-03.mp3",
|
|
6
|
+
permission: "staplebops-06.mp3",
|
|
7
|
+
error: "nope-03.mp3",
|
|
8
|
+
done: "bip-bop-01.mp3",
|
|
9
|
+
subagent_done: "yup-01.mp3",
|
|
10
|
+
};
|
|
11
|
+
export const VIBRATE_PATTERNS = {
|
|
12
|
+
default: "400,200,400",
|
|
13
|
+
done: "400,200,400",
|
|
14
|
+
subagent_done: "200,100,200",
|
|
15
|
+
question: "500,250,500",
|
|
16
|
+
permission: "600,200,600",
|
|
17
|
+
error: "800,300,800,300,800",
|
|
18
|
+
};
|
|
19
|
+
export const PRIORITY_BY_KIND = {
|
|
20
|
+
default: "high",
|
|
21
|
+
done: "high",
|
|
22
|
+
subagent_done: "default",
|
|
23
|
+
question: "high",
|
|
24
|
+
permission: "high",
|
|
25
|
+
error: "high",
|
|
26
|
+
};
|
|
27
|
+
export const DEFAULTS = {
|
|
28
|
+
bin: "/data/data/com.termux/files/usr/bin/termux-notification",
|
|
29
|
+
mediaBin: "/data/data/com.termux/files/usr/bin/termux-media-player",
|
|
30
|
+
sharedPath: join(tmpdir(), "termux-notify-shared.json"),
|
|
31
|
+
seenTTL: 60_000,
|
|
32
|
+
sessionCooldown: 5_000,
|
|
33
|
+
globalCooldown: 1_000,
|
|
34
|
+
sound: true,
|
|
35
|
+
playSound: true,
|
|
36
|
+
vibrate: true,
|
|
37
|
+
requireTermux: true,
|
|
38
|
+
priority: undefined,
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,OAAO,EAAE,gBAAgB;IACzB,QAAQ,EAAE,gBAAgB;IAC1B,UAAU,EAAE,mBAAmB;IAC/B,KAAK,EAAE,aAAa;IACpB,IAAI,EAAE,gBAAgB;IACtB,aAAa,EAAE,YAAY;CAC5B,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,OAAO,EAAE,aAAa;IACtB,IAAI,EAAE,aAAa;IACnB,aAAa,EAAE,aAAa;IAC5B,QAAQ,EAAE,aAAa;IACvB,UAAU,EAAE,aAAa;IACzB,KAAK,EAAE,qBAAqB;CAC7B,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,OAAO,EAAE,MAAM;IACf,IAAI,EAAE,MAAM;IACZ,aAAa,EAAE,SAAS;IACxB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,KAAK,EAAE,MAAM;CACd,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,GAAG,EAAE,yDAAyD;IAC9D,QAAQ,EAAE,yDAAyD;IACnE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,2BAA2B,CAAC;IACvD,OAAO,EAAE,MAAM;IACf,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,KAAK;IACrB,KAAK,EAAE,IAAI;IACX,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,IAAI;IACnB,QAAQ,EAAE,SAA+B;CAC1C,CAAA"}
|
package/dist/dedup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAQ9C,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAgDhH"}
|
package/dist/dedup.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFile, writeFile, rename } from "node:fs/promises";
|
|
2
|
+
export async function shouldNotifyShared(evtId, sessionKey, opts) {
|
|
3
|
+
const now = Date.now();
|
|
4
|
+
let state = {
|
|
5
|
+
seen: {},
|
|
6
|
+
lastBySession: {},
|
|
7
|
+
lastGlobal: 0,
|
|
8
|
+
};
|
|
9
|
+
try {
|
|
10
|
+
const raw = await readFile(opts.sharedPath, "utf8");
|
|
11
|
+
state = JSON.parse(raw);
|
|
12
|
+
for (const [k, exp] of Object.entries(state.seen || {})) {
|
|
13
|
+
if (now > exp)
|
|
14
|
+
delete state.seen[k];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
18
|
+
state.seen ??= {};
|
|
19
|
+
state.lastBySession ??= {};
|
|
20
|
+
if (evtId && state.seen[evtId] && now < state.seen[evtId])
|
|
21
|
+
return false;
|
|
22
|
+
const lastForKey = state.lastBySession[sessionKey] || 0;
|
|
23
|
+
if (now - lastForKey < opts.sessionCooldown)
|
|
24
|
+
return false;
|
|
25
|
+
if (now - (state.lastGlobal || 0) < opts.globalCooldown)
|
|
26
|
+
return false;
|
|
27
|
+
if (evtId)
|
|
28
|
+
state.seen[evtId] = now + opts.seenTTL;
|
|
29
|
+
state.lastBySession[sessionKey] = now;
|
|
30
|
+
state.lastGlobal = now;
|
|
31
|
+
const seenKeys = Object.keys(state.seen);
|
|
32
|
+
if (seenKeys.length > 300) {
|
|
33
|
+
const sorted = seenKeys.sort((a, b) => state.seen[b] - state.seen[a]);
|
|
34
|
+
const keep = new Set(sorted.slice(0, 200));
|
|
35
|
+
for (const k of seenKeys)
|
|
36
|
+
if (!keep.has(k))
|
|
37
|
+
delete state.seen[k];
|
|
38
|
+
}
|
|
39
|
+
const sessionKeys = Object.keys(state.lastBySession);
|
|
40
|
+
if (sessionKeys.length > 200) {
|
|
41
|
+
const cutoff = now - opts.sessionCooldown * 4;
|
|
42
|
+
for (const k of sessionKeys)
|
|
43
|
+
if (state.lastBySession[k] < cutoff)
|
|
44
|
+
delete state.lastBySession[k];
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const tmp = opts.sharedPath + ".tmp." + process.pid;
|
|
48
|
+
await writeFile(tmp, JSON.stringify(state), "utf8");
|
|
49
|
+
await rename(tmp, opts.sharedPath);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
53
|
+
console.error("[termux-notify] shared state write failed:", msg);
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=dedup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.js","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"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"}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C"}
|
package/dist/env.js
ADDED
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB;IACjC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAA;AAC1F,CAAC"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NotifyKind } from "./types.js";
|
|
2
|
+
export declare function classifyEvent(event: any, ctx: any): Promise<{
|
|
3
|
+
kind: NotifyKind;
|
|
4
|
+
sound: string;
|
|
5
|
+
sessionID: string;
|
|
6
|
+
evtId: string;
|
|
7
|
+
isSubagent: boolean;
|
|
8
|
+
} | null>;
|
|
9
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,wBAAsB,aAAa,CACjC,KAAK,EAAE,GAAG,EACV,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAAC,CAoD5G"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export async function classifyEvent(event, ctx) {
|
|
2
|
+
const t = event?.type;
|
|
3
|
+
if (!t)
|
|
4
|
+
return null;
|
|
5
|
+
const sessionID = event.properties?.sessionID || event.data?.sessionID || event.sessionID || event.properties?.id || "global";
|
|
6
|
+
const evtId = event.id || event.eventID || `${t}:${sessionID}:${event.created || ""}:${event.properties?.id || event.properties?.requestID || ""}`;
|
|
7
|
+
if (t === "question.asked" || t === "question.v2.asked" || t === "question.updated" || t.startsWith("question")) {
|
|
8
|
+
return { kind: "question", sound: "question", sessionID, evtId, isSubagent: false };
|
|
9
|
+
}
|
|
10
|
+
if (t === "permission.asked" || t === "permission.v2.asked" || t.startsWith("permission")) {
|
|
11
|
+
return { kind: "permission", sound: "permission", sessionID, evtId, isSubagent: false };
|
|
12
|
+
}
|
|
13
|
+
if (t === "session.error" ||
|
|
14
|
+
t === "session.execution.failed" ||
|
|
15
|
+
t === "session.step.failed" ||
|
|
16
|
+
t === "error" ||
|
|
17
|
+
t.endsWith(".error")) {
|
|
18
|
+
return { kind: "error", sound: "error", sessionID, evtId, isSubagent: false };
|
|
19
|
+
}
|
|
20
|
+
if (t === "session.status" && event.properties?.status?.type === "idle") {
|
|
21
|
+
let isSubagent = false;
|
|
22
|
+
try {
|
|
23
|
+
if (ctx?.session?.get) {
|
|
24
|
+
const info = await ctx.session.get({ sessionID });
|
|
25
|
+
const raw = info?.info || info?.data || info;
|
|
26
|
+
isSubagent = Boolean(raw?.parentID || raw?.parentId || raw?.parent);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
if (!isSubagent && event.properties?.parentID)
|
|
31
|
+
isSubagent = true;
|
|
32
|
+
const sound = isSubagent ? "subagent_done" : "done";
|
|
33
|
+
const kind = isSubagent ? "subagent_done" : "done";
|
|
34
|
+
return { kind, sound, sessionID, evtId, isSubagent };
|
|
35
|
+
}
|
|
36
|
+
if (t === "session.idle" || t === "session.execution.succeeded") {
|
|
37
|
+
let isSubagent = false;
|
|
38
|
+
try {
|
|
39
|
+
if (ctx?.session?.get) {
|
|
40
|
+
const info = await ctx.session.get({ sessionID });
|
|
41
|
+
const raw = info?.info || info?.data || info;
|
|
42
|
+
isSubagent = Boolean(raw?.parentID || raw?.parentId);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
const sound = isSubagent ? "subagent_done" : "done";
|
|
47
|
+
const kind = isSubagent ? "subagent_done" : "done";
|
|
48
|
+
return { kind, sound, sessionID, evtId, isSubagent };
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opencode-termux-notify — Termux-native notifications for OpenCode (V2)
|
|
3
|
+
*
|
|
4
|
+
* 6 builtin attention sounds:
|
|
5
|
+
* default → bip-bop-01.mp3
|
|
6
|
+
* question → bip-bop-03.mp3
|
|
7
|
+
* permission → staplebops-06.mp3
|
|
8
|
+
* error → nope-03.mp3
|
|
9
|
+
* done → bip-bop-01.mp3
|
|
10
|
+
* subagent_done → yup-01.mp3
|
|
11
|
+
*
|
|
12
|
+
* Audio bundled at assets/audio/*.mp3, played via `termux-media-player play <file>`
|
|
13
|
+
* (termux-notification --sound is boolean only — no file arg)
|
|
14
|
+
*/
|
|
15
|
+
export type { TermuxNotifyOptions } from "./types.js";
|
|
16
|
+
export { DEFAULTS, PRIORITY_BY_KIND, SOUND_FILES, VIBRATE_PATTERNS } from "./constants.js";
|
|
17
|
+
declare const _default: import("@opencode-ai/plugin/v2/promise").Plugin;
|
|
18
|
+
export default _default;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA"}
|