claude-notification-plugin 1.0.104 → 1.0.105
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/.claude-plugin/plugin.json +1 -1
- package/bin/uninstall.js +155 -45
- package/commit-sha +1 -1
- package/listener/LISTENER-DETAILED.md +1 -1
- package/listener/listener.js +14 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.105",
|
|
4
4
|
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Viacheslav Makarov",
|
package/bin/uninstall.js
CHANGED
|
@@ -16,56 +16,166 @@ const HOOK_COMMAND = 'claude-notify';
|
|
|
16
16
|
const PLUGIN_KEY = 'claude-notification-plugin@bazilio-plugins';
|
|
17
17
|
const MARKETPLACE_KEY = 'bazilio-plugins';
|
|
18
18
|
|
|
19
|
-
function isPluginHookCommand (command) {
|
|
20
|
-
if (typeof command !== 'string') {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
19
|
+
function isPluginHookCommand (command) {
|
|
20
|
+
if (typeof command !== 'string') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
23
|
|
|
24
24
|
const normalized = command.trim().toLowerCase();
|
|
25
25
|
if (!normalized) {
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
return normalized === HOOK_COMMAND || normalized.startsWith(`${HOOK_COMMAND} `);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
28
|
+
|
|
29
|
+
return normalized === HOOK_COMMAND || normalized.startsWith(`${HOOK_COMMAND} `);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isProcessAlive (pid) {
|
|
33
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
if (process.platform === 'win32') {
|
|
39
|
+
const result = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
|
|
40
|
+
encoding: 'utf-8',
|
|
41
|
+
windowsHide: true,
|
|
42
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
43
|
+
});
|
|
44
|
+
return result.includes(String(pid));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
process.kill(pid, 0);
|
|
48
|
+
return true;
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function killProcessTree (pid) {
|
|
55
|
+
if (!isProcessAlive(pid)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
if (process.platform === 'win32') {
|
|
61
|
+
execSync(`taskkill /PID ${pid} /T /F`, {
|
|
62
|
+
stdio: 'ignore',
|
|
63
|
+
windowsHide: true,
|
|
64
|
+
});
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
process.kill(pid, 'SIGTERM');
|
|
69
|
+
let tries = 10;
|
|
70
|
+
while (tries-- > 0 && isProcessAlive(pid)) {
|
|
71
|
+
execSync('sleep 0.2', { stdio: 'ignore' });
|
|
72
|
+
}
|
|
73
|
+
if (isProcessAlive(pid)) {
|
|
74
|
+
process.kill(pid, 'SIGKILL');
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function readPid (filePath) {
|
|
83
|
+
try {
|
|
84
|
+
if (!fs.existsSync(filePath)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const pid = parseInt(fs.readFileSync(filePath, 'utf-8').trim(), 10);
|
|
88
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function removeFileIfExists (filePath) {
|
|
95
|
+
try {
|
|
96
|
+
if (fs.existsSync(filePath)) {
|
|
97
|
+
fs.unlinkSync(filePath);
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
// ignore
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function findListenerPids () {
|
|
105
|
+
try {
|
|
106
|
+
if (process.platform === 'win32') {
|
|
107
|
+
const raw = execSync(
|
|
108
|
+
'powershell -NoProfile -Command "Get-CimInstance Win32_Process | Select-Object ProcessId, CommandLine | ConvertTo-Json -Compress"',
|
|
109
|
+
{
|
|
110
|
+
encoding: 'utf-8',
|
|
111
|
+
windowsHide: true,
|
|
112
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
113
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
114
|
+
},
|
|
115
|
+
).trim();
|
|
116
|
+
|
|
117
|
+
if (!raw) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const rows = JSON.parse(raw);
|
|
122
|
+
const processes = Array.isArray(rows) ? rows : [rows];
|
|
123
|
+
return processes
|
|
124
|
+
.filter((row) => /claude-notification-plugin[\\/]+listener[\\/]+listener\.js/i.test(row?.CommandLine || ''))
|
|
125
|
+
.map((row) => Number(row?.ProcessId))
|
|
126
|
+
.filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const raw = execSync('ps -eo pid=,args=', {
|
|
130
|
+
encoding: 'utf-8',
|
|
131
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
132
|
+
});
|
|
133
|
+
const pids = [];
|
|
134
|
+
for (const line of raw.split('\n')) {
|
|
135
|
+
const match = line.trim().match(/^(\d+)\s+([\s\S]+)$/);
|
|
136
|
+
if (!match) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const pid = parseInt(match[1], 10);
|
|
141
|
+
const args = match[2];
|
|
142
|
+
if (/claude-notification-plugin[\\/]+listener[\\/]+listener\.js/i.test(args)) {
|
|
143
|
+
pids.push(pid);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return pids;
|
|
147
|
+
} catch {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function stopListenerIfRunning () {
|
|
153
|
+
let stopped = false;
|
|
154
|
+
const processed = new Set();
|
|
155
|
+
|
|
156
|
+
const pidFromFile = readPid(pidFile);
|
|
157
|
+
if (pidFromFile) {
|
|
158
|
+
if (killProcessTree(pidFromFile)) {
|
|
159
|
+
stopped = true;
|
|
160
|
+
}
|
|
161
|
+
processed.add(pidFromFile);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const pid of findListenerPids()) {
|
|
165
|
+
if (processed.has(pid)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (killProcessTree(pid)) {
|
|
169
|
+
stopped = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
removeFileIfExists(pidFile);
|
|
174
|
+
return stopped;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Stop listener daemon if running
|
|
178
|
+
const listenerStopped = stopListenerIfRunning();
|
|
69
179
|
|
|
70
180
|
// Remove hooks from settings.json
|
|
71
181
|
let hooksRemoved = false;
|
package/commit-sha
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
a9b07bbf2e32a077e4f17b6ccad9b2e4c6979563
|
package/listener/listener.js
CHANGED
|
@@ -195,7 +195,20 @@ function formatLabel (entry) {
|
|
|
195
195
|
async function startTask (workDir, task) {
|
|
196
196
|
const entry = queue.queues[workDir];
|
|
197
197
|
const label = formatLabel(entry);
|
|
198
|
-
const
|
|
198
|
+
const runningShort = `⏳ [${label}] Running...`;
|
|
199
|
+
const runningFull = `⏳ [${label}] Running: ${escapeHtml(task.text)}`;
|
|
200
|
+
let runningMsgId = null;
|
|
201
|
+
|
|
202
|
+
if (task.telegramMessageId) {
|
|
203
|
+
// In replies, the quoted user message already contains task text.
|
|
204
|
+
runningMsgId = await poller.sendMessage(runningShort, task.telegramMessageId);
|
|
205
|
+
if (!runningMsgId) {
|
|
206
|
+
runningMsgId = await poller.sendMessage(runningFull);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
runningMsgId = await poller.sendMessage(runningFull);
|
|
210
|
+
}
|
|
211
|
+
|
|
199
212
|
task.runningMessageId = runningMsgId;
|
|
200
213
|
try {
|
|
201
214
|
const started = runner.run(workDir, task);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
3
|
"productName": "claude-notification-plugin",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.105",
|
|
5
5
|
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|