zjbar-opencode 0.1.0 → 0.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/dist/index.js +117 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
1
4
|
// src/index.ts
|
|
5
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { homedir, platform } from "node:os";
|
|
8
|
+
import { spawn, execFile as execFileCb } from "node:child_process";
|
|
2
9
|
var TOOL_MAP = {
|
|
3
10
|
bash: "Bash",
|
|
4
11
|
read: "Read",
|
|
@@ -14,14 +21,100 @@ var TOOL_MAP = {
|
|
|
14
21
|
function capitalize(s) {
|
|
15
22
|
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
16
23
|
}
|
|
17
|
-
var
|
|
24
|
+
var DEFAULT_NOTIFY_EVENTS = ["PermissionRequest", "Notification", "Stop"];
|
|
25
|
+
var NOTIFY_RATE_LIMIT_SECS = 10;
|
|
26
|
+
var lastNotifyTime = {};
|
|
27
|
+
function loadSettings() {
|
|
28
|
+
const settingsPath = join(homedir(), ".config", "zellij", "plugins", "zjbar.json");
|
|
29
|
+
try {
|
|
30
|
+
if (existsSync(settingsPath)) {
|
|
31
|
+
return JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
32
|
+
}
|
|
33
|
+
} catch {}
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
function shouldNotify(hookEvent, paneId, termProgram) {
|
|
37
|
+
const settings = loadSettings();
|
|
38
|
+
const mode = settings.notifications ?? "always";
|
|
39
|
+
if (mode === "off")
|
|
40
|
+
return false;
|
|
41
|
+
const events = settings.notify_events ?? DEFAULT_NOTIFY_EVENTS;
|
|
42
|
+
if (!events.includes(hookEvent))
|
|
43
|
+
return false;
|
|
44
|
+
const now = Math.floor(Date.now() / 1000);
|
|
45
|
+
const key = `pane-${paneId}`;
|
|
46
|
+
if (now - (lastNotifyTime[key] ?? 0) < NOTIFY_RATE_LIMIT_SECS)
|
|
47
|
+
return false;
|
|
48
|
+
lastNotifyTime[key] = now;
|
|
49
|
+
if (mode === "unfocused" && platform() === "darwin" && termProgram) {
|
|
50
|
+
try {
|
|
51
|
+
const { execSync } = __require("node:child_process");
|
|
52
|
+
const frontApp = execSync(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`, { encoding: "utf-8" }).trim();
|
|
53
|
+
let expected = termProgram;
|
|
54
|
+
if (expected === "Apple_Terminal")
|
|
55
|
+
expected = "Terminal";
|
|
56
|
+
if (expected === "iTerm.app")
|
|
57
|
+
expected = "iTerm2";
|
|
58
|
+
if (frontApp === expected)
|
|
59
|
+
return false;
|
|
60
|
+
} catch {}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
function sendNotification(hookEvent, paneId, zellijSession, termProgram) {
|
|
65
|
+
const appName = "OpenCode";
|
|
66
|
+
const iconFile = "opencode-logo.png";
|
|
67
|
+
const pluginDir = join(homedir(), ".config", "zellij", "plugins");
|
|
68
|
+
const iconPath = join(pluginDir, iconFile);
|
|
69
|
+
let title;
|
|
70
|
+
let message;
|
|
71
|
+
switch (hookEvent) {
|
|
72
|
+
case "PermissionRequest":
|
|
73
|
+
title = `⚠ ${appName}`;
|
|
74
|
+
message = "Permission requested";
|
|
75
|
+
break;
|
|
76
|
+
case "Stop":
|
|
77
|
+
title = `✅ ${appName}`;
|
|
78
|
+
message = "Task completed";
|
|
79
|
+
break;
|
|
80
|
+
case "Notification":
|
|
81
|
+
title = appName;
|
|
82
|
+
message = "Notification received";
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
title = appName;
|
|
86
|
+
message = `Event: ${hookEvent}`;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
if (platform() === "darwin") {
|
|
90
|
+
if (hookEvent === "PermissionRequest") {
|
|
91
|
+
process.stdout.write("\x07");
|
|
92
|
+
}
|
|
93
|
+
const focusCmd = termProgram ? `open -a '${termProgram}' && zellij -s '${zellijSession}' pipe --name zjbar:focus -- ${paneId}` : `zellij -s '${zellijSession}' pipe --name zjbar:focus -- ${paneId}`;
|
|
94
|
+
const args = ["-title", title, "-message", message, "-execute", focusCmd];
|
|
95
|
+
if (existsSync(iconPath)) {
|
|
96
|
+
args.unshift("-contentImage", iconPath);
|
|
97
|
+
}
|
|
98
|
+
execFileCb("terminal-notifier", args, (err) => {
|
|
99
|
+
if (err) {
|
|
100
|
+
execFileCb("osascript", [
|
|
101
|
+
"-e",
|
|
102
|
+
`display notification "${message.replace(/"/g, "\\\"")}" with title "${title.replace(/"/g, "\\\"")}"`
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
} else if (platform() === "linux") {
|
|
107
|
+
execFileCb("notify-send", [title, message]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var ZjbarPlugin = async ({ directory }) => {
|
|
18
111
|
const zellijSession = process.env.ZELLIJ_SESSION_NAME;
|
|
19
112
|
const paneId = process.env.ZELLIJ_PANE_ID;
|
|
20
113
|
if (!zellijSession || !paneId)
|
|
21
114
|
return {};
|
|
22
115
|
const sessionId = crypto.randomUUID();
|
|
23
116
|
const termProgram = process.env.TERM_PROGRAM || null;
|
|
24
|
-
|
|
117
|
+
function sendToZjbar(hookEvent, toolName) {
|
|
25
118
|
const payload = JSON.stringify({
|
|
26
119
|
source: "opencode",
|
|
27
120
|
pane_id: parseInt(paneId, 10),
|
|
@@ -32,37 +125,47 @@ var ZjbarPlugin = async ({ $, directory }) => {
|
|
|
32
125
|
zellij_session: zellijSession,
|
|
33
126
|
term_program: termProgram
|
|
34
127
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
128
|
+
const child = spawn("zellij", [
|
|
129
|
+
"-s",
|
|
130
|
+
zellijSession,
|
|
131
|
+
"pipe",
|
|
132
|
+
"--name",
|
|
133
|
+
"zjbar",
|
|
134
|
+
"--",
|
|
135
|
+
payload
|
|
136
|
+
], { detached: true, stdio: "ignore" });
|
|
137
|
+
child.unref();
|
|
138
|
+
if (shouldNotify(hookEvent, paneId, termProgram)) {
|
|
139
|
+
sendNotification(hookEvent, paneId, zellijSession, termProgram);
|
|
140
|
+
}
|
|
38
141
|
}
|
|
39
|
-
|
|
142
|
+
sendToZjbar("SessionStart");
|
|
40
143
|
return {
|
|
41
144
|
event: async ({ event }) => {
|
|
42
145
|
switch (event.type) {
|
|
43
146
|
case "session.created":
|
|
44
|
-
|
|
147
|
+
sendToZjbar("SessionStart");
|
|
45
148
|
break;
|
|
46
149
|
case "session.idle":
|
|
47
|
-
|
|
150
|
+
sendToZjbar("Stop");
|
|
48
151
|
break;
|
|
49
152
|
case "session.deleted":
|
|
50
|
-
|
|
153
|
+
sendToZjbar("SessionEnd");
|
|
51
154
|
break;
|
|
52
155
|
case "permission.asked":
|
|
53
|
-
|
|
156
|
+
sendToZjbar("PermissionRequest");
|
|
54
157
|
break;
|
|
55
|
-
case "message.
|
|
56
|
-
|
|
158
|
+
case "message.created":
|
|
159
|
+
sendToZjbar("UserPromptSubmit");
|
|
57
160
|
break;
|
|
58
161
|
}
|
|
59
162
|
},
|
|
60
163
|
"tool.execute.before": async (input) => {
|
|
61
164
|
const toolName = TOOL_MAP[input.tool] || capitalize(input.tool);
|
|
62
|
-
|
|
165
|
+
sendToZjbar("PreToolUse", toolName);
|
|
63
166
|
},
|
|
64
167
|
"tool.execute.after": async () => {
|
|
65
|
-
|
|
168
|
+
sendToZjbar("PostToolUse");
|
|
66
169
|
}
|
|
67
170
|
};
|
|
68
171
|
};
|