opencode-plugin-apprise 1.0.0 → 1.2.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.
@@ -1,4 +1,4 @@
1
1
  import type { Hooks, PluginInput } from "@opencode-ai/plugin";
2
2
  import type { DedupChecker } from "../dedup.js";
3
3
  import type { PluginConfig } from "../types.js";
4
- export declare function createIdleHook(ctx: PluginInput, config: PluginConfig, dedup: DedupChecker): NonNullable<Hooks["event"]>;
4
+ export declare function createIdleHook(ctx: PluginInput, config: PluginConfig, dedup: DedupChecker, interactiveSessions: Set<string>): NonNullable<Hooks["event"]>;
@@ -4,7 +4,7 @@ import type { PluginConfig } from "../types.js";
4
4
  export interface PermissionHooks {
5
5
  /** Primary: permission.ask hook */
6
6
  permissionAsk: NonNullable<Hooks["permission.ask"]>;
7
- /** Fallback: event hook watching permission.updated */
7
+ /** Fallback: event hook watching permission.asked */
8
8
  eventFallback: NonNullable<Hooks["event"]>;
9
9
  }
10
10
  export declare function createPermissionHooks(config: PluginConfig, dedup: DedupChecker): PermissionHooks;
@@ -1,8 +1,4 @@
1
1
  import type { Hooks } from "@opencode-ai/plugin";
2
2
  import type { DedupChecker } from "../dedup.js";
3
3
  import type { PluginConfig } from "../types.js";
4
- export interface QuestionHooks {
5
- before: NonNullable<Hooks["tool.execute.before"]>;
6
- after: NonNullable<Hooks["tool.execute.after"]>;
7
- }
8
- export declare function createQuestionHooks(config: PluginConfig, dedup: DedupChecker, delayMs?: number): QuestionHooks;
4
+ export declare function createQuestionHook(config: PluginConfig, dedup: DedupChecker, delayMs?: number): NonNullable<Hooks["event"]>;
@@ -54,7 +54,6 @@ function createDedupChecker() {
54
54
  var TYPE_MAP = {
55
55
  idle: "info",
56
56
  question: "warning",
57
- background: "success",
58
57
  permission: "warning"
59
58
  };
60
59
  var DEFAULT_TRUNCATE_LENGTH = 1500;
@@ -127,17 +126,6 @@ ${context.options.map((option, index) => ` ${index + 1}. ${option}`).join(`
127
126
  }
128
127
  body = parts.join(`
129
128
 
130
- `);
131
- break;
132
- }
133
- case "background": {
134
- const parts = [];
135
- if (context.taskName)
136
- parts.push(`Task: ${context.taskName}`);
137
- if (context.agentResponse)
138
- parts.push(`Result: ${context.agentResponse}`);
139
- body = parts.join(`
140
-
141
129
  `);
142
130
  break;
143
131
  }
@@ -220,7 +208,6 @@ var EMPTY_CONTEXT = {
220
208
  question: undefined,
221
209
  options: undefined,
222
210
  todoStatus: undefined,
223
- taskName: undefined,
224
211
  toolName: undefined,
225
212
  action: undefined
226
213
  };
@@ -242,21 +229,6 @@ async function sendHookNotification(hookName, config, dedup, payload) {
242
229
  }
243
230
  }
244
231
 
245
- // src/hooks/background.ts
246
- function createBackgroundHook(config, dedup) {
247
- return async ({ event }) => {
248
- if (event.type !== "session.status")
249
- return;
250
- const props = event.properties;
251
- if (props.status.type !== "idle")
252
- return;
253
- const payload = createPayload("background", "✅ Background Task Complete", {
254
- taskName: `Session ${props.sessionID}`
255
- });
256
- await sendHookNotification("background", config, dedup, payload);
257
- };
258
- }
259
-
260
232
  // src/hooks/idle.ts
261
233
  function extractText(message) {
262
234
  if (!message || typeof message !== "object") {
@@ -266,13 +238,17 @@ function extractText(message) {
266
238
  return parts.join(`
267
239
  `).trim() || undefined;
268
240
  }
269
- function createIdleHook(ctx, config, dedup) {
241
+ function createIdleHook(ctx, config, dedup, interactiveSessions) {
270
242
  return async ({ event }) => {
271
- if (event.type !== "session.idle")
243
+ if (event.type !== "session.status")
272
244
  return;
273
245
  const props = event.properties;
246
+ if (props.status.type !== "idle")
247
+ return;
274
248
  if (!props.sessionID)
275
249
  return;
250
+ if (!interactiveSessions.has(props.sessionID))
251
+ return;
276
252
  let userRequest = undefined;
277
253
  let agentResponse = undefined;
278
254
  let todoStatus = undefined;
@@ -320,65 +296,71 @@ function createIdleHook(ctx, config, dedup) {
320
296
  // src/hooks/permission.ts
321
297
  function createPermissionHooks(config, dedup) {
322
298
  const notifiedPermissions = new Set;
323
- async function notifyPermission(permission) {
324
- const permId = permission.id ?? "unknown";
299
+ const permissionAsk = async (input, _output) => {
300
+ const permId = input.id ?? "unknown";
325
301
  if (notifiedPermissions.has(permId))
326
302
  return;
327
303
  notifiedPermissions.add(permId);
328
- const toolName = permission.toolName ?? "Unknown Tool";
329
- const action = permission.action ?? "Unknown Action";
304
+ const title = input.title ?? "Unknown";
305
+ const pattern = input.pattern;
306
+ const action = Array.isArray(pattern) ? pattern.join(", ") : pattern ?? "Unknown";
330
307
  const payload = createPayload("permission", "\uD83D\uDD10 OpenCode Permission Required", {
331
- toolName,
308
+ toolName: title,
332
309
  action
333
310
  });
334
311
  await sendHookNotification("permission", config, dedup, payload);
335
- }
336
- const permissionAsk = async (input, _output) => {
337
- await notifyPermission(input);
338
312
  };
339
313
  const eventFallback = async ({ event }) => {
340
- if (event.type !== "permission.updated")
314
+ const eventType = event.type;
315
+ if (eventType !== "permission.asked")
316
+ return;
317
+ const props = event.properties;
318
+ const permId = props.id ?? "unknown";
319
+ if (notifiedPermissions.has(permId))
341
320
  return;
342
- const permission = event.properties;
343
- await notifyPermission(permission);
321
+ notifiedPermissions.add(permId);
322
+ const payload = createPayload("permission", "\uD83D\uDD10 OpenCode Permission Required", {
323
+ toolName: props.permission ?? "Unknown",
324
+ action: props.patterns?.join(", ") ?? "Unknown"
325
+ });
326
+ await sendHookNotification("permission", config, dedup, payload);
344
327
  };
345
328
  return { permissionAsk, eventFallback };
346
329
  }
347
330
 
348
331
  // src/hooks/question.ts
349
- function createQuestionHooks(config, dedup, delayMs = 30000) {
332
+ function createQuestionHook(config, dedup, delayMs = 30000) {
350
333
  const timers = new Map;
351
- const before = async ({ tool, callID }, input) => {
352
- if (tool.toLowerCase() !== "question")
334
+ return async ({ event }) => {
335
+ const eventType = event.type;
336
+ if (eventType === "question.replied" || eventType === "question.rejected") {
337
+ const props2 = event.properties;
338
+ const timer2 = timers.get(props2.requestID);
339
+ if (timer2) {
340
+ clearTimeout(timer2);
341
+ timers.delete(props2.requestID);
342
+ }
353
343
  return;
354
- const args = input?.args;
355
- const question = typeof args?.question === "string" ? args.question : undefined;
356
- const options = Array.isArray(args?.options) ? args.options.filter((option) => typeof option === "string") : undefined;
344
+ }
345
+ if (eventType !== "question.asked")
346
+ return;
347
+ const props = event.properties;
348
+ const firstQuestion = props.questions[0];
349
+ if (!firstQuestion)
350
+ return;
351
+ const question = firstQuestion.question;
352
+ const options = firstQuestion.options.map((opt) => opt.label);
353
+ const requestId = props.id;
357
354
  const timer = setTimeout(async () => {
358
- if (!question)
359
- return;
355
+ timers.delete(requestId);
360
356
  const payload = createPayload("question", "❓ OpenCode Question", {
361
357
  question,
362
- options,
363
- toolName: "Question"
358
+ options: options.length > 0 ? options : undefined
364
359
  });
365
360
  await sendHookNotification("question", config, dedup, payload);
366
361
  }, delayMs);
367
- timers.set(callID, timer);
368
- };
369
- const after = async ({
370
- tool,
371
- callID
372
- }) => {
373
- if (tool.toLowerCase() !== "question")
374
- return;
375
- const timer = timers.get(callID);
376
- if (timer) {
377
- clearTimeout(timer);
378
- timers.delete(callID);
379
- }
362
+ timers.set(requestId, timer);
380
363
  };
381
- return { before, after };
382
364
  }
383
365
 
384
366
  // src/index.ts
@@ -399,20 +381,22 @@ var plugin = async (input) => {
399
381
  return {};
400
382
  }
401
383
  const dedup = createDedupChecker();
402
- const idleHook = createIdleHook(input, config, dedup);
403
- const questionHooks = createQuestionHooks(config, dedup);
404
- const backgroundHook = createBackgroundHook(config, dedup);
384
+ const interactiveSessions = new Set;
385
+ const idleHook = createIdleHook(input, config, dedup, interactiveSessions);
386
+ const questionHook = createQuestionHook(config, dedup);
405
387
  const permissionHooks = createPermissionHooks(config, dedup);
406
388
  const combinedEventHook = async ({ event }) => {
407
- await idleHook({ event });
408
- await backgroundHook({ event });
389
+ await questionHook({ event });
409
390
  await permissionHooks.eventFallback({ event });
391
+ await idleHook({ event });
392
+ };
393
+ const chatMessageHook = async (input2) => {
394
+ interactiveSessions.add(input2.sessionID);
410
395
  };
411
396
  return {
412
397
  event: combinedEventHook,
413
- "tool.execute.before": questionHooks.before,
414
- "tool.execute.after": questionHooks.after,
415
- "permission.ask": permissionHooks.permissionAsk
398
+ "permission.ask": permissionHooks.permissionAsk,
399
+ "chat.message": chatMessageHook
416
400
  };
417
401
  };
418
402
  var src_default = plugin;
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export interface PluginConfig {
2
2
  tag?: string;
3
3
  }
4
- export type HookEventType = "idle" | "question" | "background" | "permission";
4
+ export type HookEventType = "idle" | "question" | "permission";
5
5
  export type AppriseNotificationType = "info" | "warning" | "success" | "failure";
6
6
  export interface NotificationContext {
7
7
  userRequest: string | undefined;
@@ -9,7 +9,6 @@ export interface NotificationContext {
9
9
  question: string | undefined;
10
10
  options: string[] | undefined;
11
11
  todoStatus: string | undefined;
12
- taskName: string | undefined;
13
12
  toolName: string | undefined;
14
13
  action: string | undefined;
15
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-plugin-apprise",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "OpenCode plugin that sends rich notifications via Apprise CLI when the agent needs your attention",
5
5
  "type": "module",
6
6
  "main": "dist/opencode-plugin-apprise.js",
@@ -1,4 +0,0 @@
1
- import type { Hooks } from "@opencode-ai/plugin";
2
- import type { DedupChecker } from "../dedup.js";
3
- import type { PluginConfig } from "../types.js";
4
- export declare function createBackgroundHook(config: PluginConfig, dedup: DedupChecker): NonNullable<Hooks["event"]>;