@highflame/overwatch-v2 2.0.0-internal.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.
@@ -0,0 +1,20 @@
1
+ {
2
+ "UserPromptSubmit": [
3
+ {
4
+ "command": "~/.overwatch/universal-hook.sh claudecode UserPromptSubmit",
5
+ "timeout": 10000
6
+ }
7
+ ],
8
+ "PreToolUse": [
9
+ {
10
+ "command": "~/.overwatch/universal-hook.sh claudecode PreToolUse",
11
+ "timeout": 10000
12
+ }
13
+ ],
14
+ "PostToolUse": [
15
+ {
16
+ "command": "~/.overwatch/universal-hook.sh claudecode PostToolUse",
17
+ "timeout": 5000
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "beforeSubmitPrompt": [
3
+ {
4
+ "command": "~/.overwatch/universal-hook.sh cursor beforeSubmitPrompt",
5
+ "timeout": 10000
6
+ }
7
+ ],
8
+ "beforeShellExecution": [
9
+ {
10
+ "command": "~/.overwatch/universal-hook.sh cursor beforeShellExecution",
11
+ "timeout": 10000
12
+ }
13
+ ],
14
+ "beforeMCPExecution": [
15
+ {
16
+ "command": "~/.overwatch/universal-hook.sh cursor beforeMCPExecution",
17
+ "timeout": 10000
18
+ }
19
+ ],
20
+ "beforeReadFile": [
21
+ {
22
+ "command": "~/.overwatch/universal-hook.sh cursor beforeReadFile",
23
+ "timeout": 10000
24
+ }
25
+ ],
26
+ "afterShellExecution": [
27
+ {
28
+ "command": "~/.overwatch/universal-hook.sh cursor afterShellExecution",
29
+ "timeout": 5000
30
+ }
31
+ ],
32
+ "afterMCPExecution": [
33
+ {
34
+ "command": "~/.overwatch/universal-hook.sh cursor afterMCPExecution",
35
+ "timeout": 5000
36
+ }
37
+ ],
38
+ "afterFileEdit": [
39
+ {
40
+ "command": "~/.overwatch/universal-hook.sh cursor afterFileEdit",
41
+ "timeout": 5000
42
+ }
43
+ ],
44
+ "afterAgentResponse": [
45
+ {
46
+ "command": "~/.overwatch/universal-hook.sh cursor afterAgentResponse",
47
+ "timeout": 5000
48
+ }
49
+ ],
50
+ "afterAgentThought": [
51
+ {
52
+ "command": "~/.overwatch/universal-hook.sh cursor afterAgentThought",
53
+ "timeout": 5000
54
+ }
55
+ ],
56
+ "stop": [
57
+ {
58
+ "command": "~/.overwatch/universal-hook.sh cursor stop",
59
+ "timeout": 5000
60
+ }
61
+ ]
62
+ }
@@ -0,0 +1,118 @@
1
+ # Overwatch Universal Hook Adapter (PowerShell)
2
+ # This script handles communication between AI tools and the Guardian daemon.
3
+ # Usage: pwsh -NoProfile -File universal-hook.ps1 <source> <event> [default_json]
4
+ #
5
+ # Input: JSON payload via stdin
6
+ # Output: JSON response to stdout
7
+
8
+ param(
9
+ [Parameter(Mandatory=$true, Position=0)]
10
+ [string]$IdeSource,
11
+
12
+ [Parameter(Mandatory=$true, Position=1)]
13
+ [string]$HookEvent,
14
+
15
+ [Parameter(Position=2)]
16
+ [string]$DefaultResponse = "{}"
17
+ )
18
+
19
+ $ErrorActionPreference = "SilentlyContinue"
20
+
21
+ function Try-AutoStartDaemon {
22
+ $lockDir = Join-Path $env:USERPROFILE ".overwatch" "daemon.lock.d"
23
+
24
+ # Check if overwatch is on PATH
25
+ $overwatch = Get-Command "overwatch" -ErrorAction SilentlyContinue
26
+ if (-not $overwatch) { return }
27
+
28
+ # Clear stale lock first so we can start the daemon in this invocation
29
+ if (Test-Path $lockDir) {
30
+ $lockAge = (Get-Date) - (Get-Item $lockDir).LastWriteTime
31
+ if ($lockAge.TotalSeconds -gt 30) {
32
+ Remove-Item $lockDir -Force -ErrorAction SilentlyContinue
33
+ }
34
+ }
35
+
36
+ if (-not (Test-Path $lockDir)) {
37
+ # Create lock directory atomically
38
+ try {
39
+ [System.IO.Directory]::CreateDirectory($lockDir) | Out-Null
40
+ # Fire-and-forget: start daemon in background
41
+ Start-Process -FilePath "overwatch" -ArgumentList "start","--hook" `
42
+ -WindowStyle Hidden -PassThru | Out-Null
43
+ } catch {
44
+ # Another process grabbed the lock — ignore
45
+ }
46
+ }
47
+ }
48
+
49
+ # 1. Read guardian port from discovery file, default to 17580
50
+ $portFile = Join-Path $env:USERPROFILE ".overwatch" "guardian_port"
51
+ $guardianPort = 17580
52
+ if (Test-Path $portFile) {
53
+ $portStr = (Get-Content $portFile -Raw -ErrorAction SilentlyContinue).Trim()
54
+ $parsed = 0
55
+ if ([int]::TryParse($portStr, [ref]$parsed)) {
56
+ $guardianPort = $parsed
57
+ }
58
+ }
59
+
60
+ $baseUrl = "http://127.0.0.1:$guardianPort"
61
+ $endpoint = "$baseUrl/hook/$IdeSource/$HookEvent"
62
+
63
+ # 2. Read payload from stdin with timeout
64
+ # Use OpenStandardInput() instead of [Console]::In — PowerShell's host can
65
+ # redirect Console.In when launched with -File, causing piped stdin to be lost.
66
+ # OpenStandardInput() bypasses the host and reads the raw OS stdin stream.
67
+ # Wrap in a Task with 5s timeout to prevent blocking the IDE if the pipe isn't closed.
68
+ $payload = ""
69
+ try {
70
+ $stream = [System.Console]::OpenStandardInput()
71
+ $reader = [System.IO.StreamReader]::new($stream, [System.Text.Encoding]::UTF8)
72
+ $task = $reader.ReadToEndAsync()
73
+ if ($task.Wait(5000)) {
74
+ $payload = $task.Result
75
+ }
76
+ $reader.Dispose()
77
+ } catch {
78
+ $payload = ""
79
+ }
80
+ if ([string]::IsNullOrWhiteSpace($payload)) {
81
+ $payload = "{}"
82
+ }
83
+
84
+ # 3. Cross-IDE dedup: skip if Cursor is firing a non-cursor hook
85
+ if ($IdeSource -ne "cursor" -and $payload -match '"cursor_version"') {
86
+ Write-Output $DefaultResponse
87
+ exit 0
88
+ }
89
+
90
+ # 4. Health check & forward payload
91
+ try {
92
+ $healthResponse = Invoke-WebRequest -Uri "$baseUrl/health" `
93
+ -Method GET -TimeoutSec 1 -UseBasicParsing -ErrorAction Stop
94
+ $healthy = $healthResponse.StatusCode -eq 200
95
+ } catch {
96
+ $healthy = $false
97
+ }
98
+
99
+ $responseBody = $DefaultResponse
100
+ if ($healthy) {
101
+ try {
102
+ $response = Invoke-WebRequest -Uri $endpoint `
103
+ -Method POST `
104
+ -Body $payload `
105
+ -ContentType "application/json" `
106
+ -TimeoutSec 10 `
107
+ -UseBasicParsing `
108
+ -ErrorAction Stop
109
+ $responseBody = $response.Content
110
+ } catch {
111
+ # keep default
112
+ }
113
+ } else {
114
+ Try-AutoStartDaemon
115
+ }
116
+
117
+ # 5. Pass-through: every supported agent reads stdout JSON.
118
+ Write-Output $responseBody
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ # Overwatch Universal Hook Adapter
3
+ # This script handles communication between AI tools and the Guardian daemon.
4
+ # Usage: ./universal-hook.sh <source> <event> <default_json>
5
+
6
+ set -euo pipefail
7
+
8
+ IDE_SOURCE=$1
9
+ HOOK_EVENT=$2
10
+ DEFAULT_RESPONSE=${3:-"{}"}
11
+
12
+ # Auto-start daemon in background if not running (fire-and-forget)
13
+ try_auto_start() {
14
+ local LOCK_DIR="$HOME/.overwatch/daemon.lock.d"
15
+ if command -v overwatch >/dev/null 2>&1; then
16
+ # Use mkdir as an atomic lock — only one process can succeed
17
+ if mkdir "$LOCK_DIR" 2>/dev/null; then
18
+ nohup overwatch start --hook >/dev/null 2>&1 &
19
+ else
20
+ # Lock exists — check if it's stale (older than 30s)
21
+ local LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCK_DIR" 2>/dev/null || stat -c %Y "$LOCK_DIR" 2>/dev/null || echo 0) ))
22
+ if [ "$LOCK_AGE" -gt 30 ]; then
23
+ rmdir "$LOCK_DIR" 2>/dev/null || true
24
+ fi
25
+ fi
26
+ fi
27
+ }
28
+
29
+ # Read guardian port from discovery file, default to static port 17580
30
+ GUARDIAN_PORT_FILE="$HOME/.overwatch/guardian_port"
31
+ GUARDIAN_PORT=$(cat "$GUARDIAN_PORT_FILE" 2>/dev/null || echo "17580")
32
+ ENDPOINT="http://127.0.0.1:${GUARDIAN_PORT}/hook/${IDE_SOURCE}/${HOOK_EVENT}"
33
+
34
+ # 2. Read payload from stdin (needed for dedup check and forwarding)
35
+ PAYLOAD=$(cat)
36
+
37
+ # 3. Cross-IDE dedup: skip if Cursor is firing a non-cursor hook.
38
+ # Cursor picks up hooks from other IDEs (e.g. ~/.claude/settings.json) and fires them,
39
+ # causing duplicate processing. Detect this via cursor_version in the payload.
40
+ if [ "$IDE_SOURCE" != "cursor" ] && echo "$PAYLOAD" | grep -q '"cursor_version"'; then
41
+ echo "$DEFAULT_RESPONSE"
42
+ exit 0
43
+ fi
44
+
45
+ # 4. Health Check & Forward Payload
46
+ # We use a 1s timeout for the health check to avoid hanging if the daemon is dead.
47
+ if curl -s -f -m 1 "http://127.0.0.1:${GUARDIAN_PORT}/health" >/dev/null 2>&1; then
48
+ # Forward payload to daemon and capture response
49
+ # Use a 10s timeout for processing
50
+ RESPONSE=$(echo "$PAYLOAD" | curl -s -m 10 -X POST "$ENDPOINT" \
51
+ -H "Content-Type: application/json" \
52
+ -d @- 2>/dev/null || echo "$DEFAULT_RESPONSE")
53
+ else
54
+ # Daemon is unreachable — try to auto-start it
55
+ try_auto_start
56
+ RESPONSE="$DEFAULT_RESPONSE"
57
+ fi
58
+
59
+ # 5. Pass-through: every supported agent reads stdout JSON.
60
+ echo "$RESPONSE"
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @highflame/overwatch-v2
3
+ *
4
+ * Security validation module for Overwatch extensions
5
+ */
6
+ export { GuardianModule } from "./module";
7
+ export * from "./types";