opencode-vibe-webhook 1.0.1 → 1.0.2

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.d.ts CHANGED
@@ -47,11 +47,23 @@ interface SessionData {
47
47
  title?: string;
48
48
  parentID?: string;
49
49
  }
50
+ interface Part {
51
+ type: string;
52
+ text?: string;
53
+ [key: string]: unknown;
54
+ }
50
55
  type Plugin = (ctx: PluginContext) => Promise<{
51
56
  event?: (args: {
52
57
  event: OpenCodeEvent;
53
58
  }) => Promise<void>;
54
59
  tool?: Record<string, ReturnType<typeof tool>>;
60
+ "command.execute.before"?: (input: {
61
+ command: string;
62
+ sessionID: string;
63
+ arguments: string;
64
+ }, output: {
65
+ parts: Part[];
66
+ }) => Promise<void>;
55
67
  }>;
56
68
  export declare const VibeWebhookPlugin: Plugin;
57
69
  export default VibeWebhookPlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAiBhD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE;YACR,GAAG,EAAE,CAAC,IAAI,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC;gBAAE,IAAI,CAAC,EAAE,WAAW,CAAA;aAAE,CAAC,CAAC;SAC1E,CAAC;KACH,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAk1BD,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;CAChD,CAAC,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,MAmB/B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAiBhD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE;YACR,GAAG,EAAE,CAAC,IAAI,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC;gBAAE,IAAI,CAAC,EAAE,WAAW,CAAA;aAAE,CAAC,CAAC;SAC1E,CAAC;KACH,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAk1BD,UAAU,IAAI;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AA+ND,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,CACzB,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAChE,MAAM,EAAE;QAAE,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,MA6B/B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -695,6 +695,233 @@ Direct webhooks will still work.`;
695
695
  }
696
696
  }
697
697
  });
698
+ async function handleVibehookCommand(args) {
699
+ const config = loadConfigFile();
700
+ const parts = args.trim().split(/\s+/);
701
+ const command = parts[0] || "status";
702
+ switch (command) {
703
+ case "status": {
704
+ const vibeStatus = config.vibeNotifyEnabled ? `✅ Enabled (${config.vibeAgentUrl})` : "❌ Disabled";
705
+ const vibeEvents = config.vibeNotifyEvents.join(", ");
706
+ const webhookCount = config.webhooks.filter((w) => w.enabled).length;
707
+ let status = `**VibeControls Webhook Plugin**
708
+
709
+ `;
710
+ status += `**VibeControls Agent**: ${vibeStatus}
711
+ `;
712
+ status += `**Events**: ${vibeEvents}
713
+ `;
714
+ status += `**Additional Webhooks**: ${webhookCount} enabled
715
+ `;
716
+ status += `**Config**: ${config.configPath}
717
+ `;
718
+ if (config.webhooks.length > 0) {
719
+ status += `
720
+ **Webhooks**:
721
+ `;
722
+ for (const w of config.webhooks) {
723
+ const icon = w.enabled ? "✅" : "❌";
724
+ status += `${icon} ${w.name} (${w.id})
725
+ `;
726
+ }
727
+ }
728
+ return status;
729
+ }
730
+ case "add": {
731
+ const url = parts[1];
732
+ const name = parts.slice(2).join(" ");
733
+ if (!url) {
734
+ return `Error: URL is required.
735
+
736
+ Usage: /vibehook add <url> [name]
737
+
738
+ Examples:
739
+ - /vibehook add https://hooks.slack.com/services/xxx
740
+ - /vibehook add https://discord.com/api/webhooks/xxx "My Discord"`;
741
+ }
742
+ const existing = config.webhooks.find((w) => w.url === url);
743
+ if (existing) {
744
+ return `Webhook already exists: ${existing.name} (${existing.id})`;
745
+ }
746
+ const webhook = {
747
+ id: generateId(),
748
+ url,
749
+ name: name || detectWebhookName(url),
750
+ enabled: true,
751
+ events: ["default"]
752
+ };
753
+ config.webhooks.push(webhook);
754
+ saveConfigFile(config);
755
+ return `✅ Webhook added!
756
+
757
+ **ID**: ${webhook.id}
758
+ **Name**: ${webhook.name}
759
+ **URL**: ${webhook.url}
760
+ **Events**: default`;
761
+ }
762
+ case "list": {
763
+ if (config.webhooks.length === 0) {
764
+ return `No additional webhooks configured.
765
+
766
+ VibeControls Agent is the primary webhook destination.
767
+ Add more with: /vibehook add <url>`;
768
+ }
769
+ const lines = config.webhooks.map((w) => {
770
+ const status = w.enabled ? "✅" : "❌";
771
+ return `${status} **${w.name}** (${w.id})
772
+ URL: ${w.url}
773
+ Events: ${w.events.join(", ")}`;
774
+ });
775
+ return `**Additional Webhooks (${config.webhooks.length})**
776
+
777
+ ${lines.join(`
778
+
779
+ `)}`;
780
+ }
781
+ case "remove": {
782
+ const id = parts[1];
783
+ if (!id) {
784
+ return `Error: ID is required.
785
+
786
+ Usage: /vibehook remove <id>
787
+
788
+ Run /vibehook list to see webhook IDs.`;
789
+ }
790
+ const index = config.webhooks.findIndex((w) => w.id === id);
791
+ if (index === -1) {
792
+ return `Webhook not found: ${id}`;
793
+ }
794
+ const removed = config.webhooks.splice(index, 1)[0];
795
+ saveConfigFile(config);
796
+ return `✅ Webhook removed: ${removed.name}`;
797
+ }
798
+ case "enable": {
799
+ const id = parts[1];
800
+ if (!id) {
801
+ return `Error: ID is required.
802
+
803
+ Usage: /vibehook enable <id>`;
804
+ }
805
+ const webhook = config.webhooks.find((w) => w.id === id);
806
+ if (!webhook) {
807
+ return `Webhook not found: ${id}`;
808
+ }
809
+ webhook.enabled = true;
810
+ saveConfigFile(config);
811
+ return `✅ Webhook enabled: ${webhook.name}`;
812
+ }
813
+ case "disable": {
814
+ const id = parts[1];
815
+ if (!id) {
816
+ return `Error: ID is required.
817
+
818
+ Usage: /vibehook disable <id>`;
819
+ }
820
+ const webhook = config.webhooks.find((w) => w.id === id);
821
+ if (!webhook) {
822
+ return `Webhook not found: ${id}`;
823
+ }
824
+ webhook.enabled = false;
825
+ saveConfigFile(config);
826
+ return `✅ Webhook disabled: ${webhook.name}`;
827
+ }
828
+ case "test": {
829
+ const testPayload = {
830
+ id: generateEventId(),
831
+ timestamp: new Date().toISOString(),
832
+ event: {
833
+ type: "test",
834
+ sessionTitle: "Test notification from OpenCode",
835
+ properties: { test: true }
836
+ },
837
+ source: {
838
+ agent: "opencode",
839
+ plugin: "opencode-vibe-webhook",
840
+ version: PLUGIN_VERSION
841
+ }
842
+ };
843
+ const id = parts[1];
844
+ if (!id) {
845
+ const vibeUrl = `${config.vibeAgentUrl}${VIBE_NOTIFY_ENDPOINT}`;
846
+ try {
847
+ const response = await fetch(vibeUrl, {
848
+ method: "POST",
849
+ headers: { "Content-Type": "application/json" },
850
+ body: JSON.stringify(testPayload),
851
+ signal: AbortSignal.timeout(5000)
852
+ });
853
+ if (response.ok) {
854
+ return `✅ VibeControls Agent is reachable!
855
+
856
+ URL: ${vibeUrl}
857
+ Webhooks configured in VibeControls will receive events.`;
858
+ } else {
859
+ return `⚠️ VibeControls Agent returned ${response.status}
860
+
861
+ URL: ${vibeUrl}
862
+ Make sure vibe-plugin-notify is installed and running.`;
863
+ }
864
+ } catch (error) {
865
+ return `❌ VibeControls Agent not reachable
866
+
867
+ URL: ${vibeUrl}
868
+ Error: ${error instanceof Error ? error.message : error}
869
+
870
+ Make sure VibeControls Agent is running with vibe-plugin-notify.`;
871
+ }
872
+ }
873
+ const webhook = config.webhooks.find((w) => w.id === id);
874
+ if (!webhook) {
875
+ return `Webhook not found: ${id}`;
876
+ }
877
+ const success = await sendWebhook(webhook.url, testPayload, config, webhook.name);
878
+ return success ? `✅ Test sent to ${webhook.name}` : `❌ Test failed for ${webhook.name}. Check the URL.`;
879
+ }
880
+ case "events": {
881
+ const presets = Object.entries(EVENT_PRESETS).map(([name, events]) => `**${name}**: ${events.join(", ")}`).join(`
882
+ `);
883
+ return `**Event Presets**
884
+
885
+ ${presets}
886
+
887
+ **Default**: Webhooks use "default" preset.
888
+
889
+ Edit ${config.configPath} to customize events per webhook.`;
890
+ }
891
+ case "vibe-enable": {
892
+ config.vibeNotifyEnabled = true;
893
+ saveConfigFile(config);
894
+ return `✅ VibeControls notifications enabled.
895
+
896
+ Events will be sent to: ${config.vibeAgentUrl}`;
897
+ }
898
+ case "vibe-disable": {
899
+ config.vibeNotifyEnabled = false;
900
+ saveConfigFile(config);
901
+ return `❌ VibeControls notifications disabled.
902
+
903
+ Direct webhooks will still work.`;
904
+ }
905
+ case "help":
906
+ default:
907
+ return `**VibeControls Webhook Plugin**
908
+
909
+ Manage webhook notifications for OpenCode events.
910
+
911
+ **Commands:**
912
+ - \`/vibehook\` or \`/vibehook status\` - Show current configuration
913
+ - \`/vibehook add <url> [name]\` - Add a webhook (Slack, Discord, custom)
914
+ - \`/vibehook list\` - List all additional webhooks
915
+ - \`/vibehook remove <id>\` - Remove a webhook
916
+ - \`/vibehook enable <id>\` - Enable a webhook
917
+ - \`/vibehook disable <id>\` - Disable a webhook
918
+ - \`/vibehook test [id]\` - Test a webhook (or VibeControls if no id)
919
+ - \`/vibehook events\` - Show available event presets
920
+ - \`/vibehook vibe-enable\` - Enable VibeControls notifications
921
+ - \`/vibehook vibe-disable\` - Disable VibeControls notifications
922
+ - \`/vibehook help\` - Show this help message`;
923
+ }
924
+ }
698
925
  var VibeWebhookPlugin = async (ctx) => {
699
926
  const config = loadConfigFile();
700
927
  log(config, `Plugin initialized (v${PLUGIN_VERSION})`);
@@ -707,6 +934,15 @@ var VibeWebhookPlugin = async (ctx) => {
707
934
  },
708
935
  tool: {
709
936
  vibehook: vibehookTool
937
+ },
938
+ "command.execute.before": async (input, output) => {
939
+ if (input.command === "vibehook") {
940
+ const result = await handleVibehookCommand(input.arguments);
941
+ output.parts.push({
942
+ type: "text",
943
+ text: result
944
+ });
945
+ }
710
946
  }
711
947
  };
712
948
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-vibe-webhook",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "OpenCode plugin for VibeControls notifications - companion to @burdenoff/vibe-plugin-notify",
5
5
  "author": {
6
6
  "name": "Vignesh T.V",