@boyingliu01/xp-gate 0.19.0 → 0.19.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/adapters/powershell.sh +31 -4
- package/gate-10.sh +105 -0
- package/lib/__tests__/npm-package.test.js +25 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/mock-policy/AGENTS.md +4 -4
- package/mutation/AGENTS.md +4 -4
- package/package.json +2 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
- package/principles/AGENTS.md +4 -4
- package/skills/delphi-review/AGENTS.md +4 -4
- package/skills/sprint-flow/AGENTS.md +4 -4
- package/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/SKILL.md +0 -47
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -65
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +0 -185
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +0 -138
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.test.cjs +0 -273
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.ps1 +0 -86
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.sh +0 -137
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
[CmdletBinding()]
|
|
2
|
-
param(
|
|
3
|
-
[switch]$AllowRemote,
|
|
4
|
-
[string]$ConfirmRemote
|
|
5
|
-
)
|
|
6
|
-
|
|
7
|
-
$ErrorActionPreference = 'Stop'
|
|
8
|
-
$ApiBase = if ($env:CLIPBOARD_VISION_API_BASE) { $env:CLIPBOARD_VISION_API_BASE } else { 'http://127.0.0.1:11434/v1' }
|
|
9
|
-
$Model = $env:CLIPBOARD_VISION_MODEL
|
|
10
|
-
$ApiKey = $env:CLIPBOARD_VISION_API_KEY
|
|
11
|
-
$MaxBytes = if ($env:CLIPBOARD_VISION_MAX_BYTES) { [long]$env:CLIPBOARD_VISION_MAX_BYTES } else { 10485760 }
|
|
12
|
-
|
|
13
|
-
if (-not $Model) { throw 'CLIPBOARD_VISION_MODEL is required' }
|
|
14
|
-
if ($MaxBytes -le 0) { throw 'CLIPBOARD_VISION_MAX_BYTES must be positive' }
|
|
15
|
-
$Endpoint = $null
|
|
16
|
-
if (-not [Uri]::TryCreate($ApiBase, [UriKind]::Absolute, [ref]$Endpoint) -or
|
|
17
|
-
-not $Endpoint.IsAbsoluteUri -or
|
|
18
|
-
$Endpoint.Scheme -notin @('http', 'https') -or
|
|
19
|
-
$Endpoint.UserInfo.Length -ne 0 -or
|
|
20
|
-
$Endpoint.Query.Length -ne 0 -or
|
|
21
|
-
$Endpoint.Fragment.Length -ne 0) {
|
|
22
|
-
throw 'CLIPBOARD_VISION_API_BASE must be an absolute HTTP(S) URI without credentials'
|
|
23
|
-
}
|
|
24
|
-
$Address = $null
|
|
25
|
-
$LiteralAddress = [Net.IPAddress]::TryParse($Endpoint.Host.Trim('[', ']'), [ref]$Address)
|
|
26
|
-
$Loopback = $LiteralAddress -and [Net.IPAddress]::IsLoopback($Address)
|
|
27
|
-
$NormalizedHost = $Endpoint.Host.Trim('[', ']').ToLowerInvariant()
|
|
28
|
-
$EffectivePort = if ($Endpoint.IsDefaultPort) { if ($Endpoint.Scheme -eq 'https') { 443 } else { 80 } } else { $Endpoint.Port }
|
|
29
|
-
$DisplayHost = if ($NormalizedHost.Contains(':')) { "[$NormalizedHost]" } else { $NormalizedHost }
|
|
30
|
-
$BasePath = $Endpoint.AbsolutePath.TrimEnd('/')
|
|
31
|
-
$EffectiveEndpoint = "$($Endpoint.Scheme.ToLowerInvariant())://$DisplayHost`:$EffectivePort$BasePath/chat/completions"
|
|
32
|
-
if (-not $Loopback) {
|
|
33
|
-
$ConsentBytes = [Text.Encoding]::UTF8.GetBytes("$EffectiveEndpoint`n$Model")
|
|
34
|
-
$Hasher = [Security.Cryptography.SHA256]::Create()
|
|
35
|
-
try { $ExpectedConfirmation = ([BitConverter]::ToString($Hasher.ComputeHash($ConsentBytes))).Replace('-', '').ToLowerInvariant() } finally { $Hasher.Dispose() }
|
|
36
|
-
[Console]::Error.WriteLine("Remote disclosure: clipboard image data will be sent to $EffectiveEndpoint using model $Model.")
|
|
37
|
-
[Console]::Error.WriteLine("Confirmation token for this canonical endpoint and model: $ExpectedConfirmation")
|
|
38
|
-
if (-not $AllowRemote -or $ConfirmRemote -cne $ExpectedConfirmation) {
|
|
39
|
-
throw 'Remote endpoint requires -AllowRemote and the disclosed -ConfirmRemote token'
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
$TempDirectory = Join-Path ([IO.Path]::GetTempPath()) ("clipboard-vision-" + [Guid]::NewGuid())
|
|
44
|
-
$ImagePath = Join-Path $TempDirectory 'clipboard.png'
|
|
45
|
-
try {
|
|
46
|
-
New-Item -ItemType Directory -Path $TempDirectory | Out-Null
|
|
47
|
-
if ($env:CLIPBOARD_VISION_TEST_IMAGE) {
|
|
48
|
-
Copy-Item -LiteralPath $env:CLIPBOARD_VISION_TEST_IMAGE -Destination $ImagePath
|
|
49
|
-
} else {
|
|
50
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
51
|
-
Add-Type -AssemblyName System.Drawing
|
|
52
|
-
$Image = [Windows.Forms.Clipboard]::GetImage()
|
|
53
|
-
if ($null -eq $Image) { throw 'Clipboard does not contain an image' }
|
|
54
|
-
try { $Image.Save($ImagePath, [Drawing.Imaging.ImageFormat]::Png) } finally { $Image.Dispose() }
|
|
55
|
-
}
|
|
56
|
-
if ((Get-Item $ImagePath).Length -gt $MaxBytes) { throw "Clipboard image exceeds $MaxBytes bytes" }
|
|
57
|
-
|
|
58
|
-
$Encoded = [Convert]::ToBase64String([IO.File]::ReadAllBytes($ImagePath))
|
|
59
|
-
$Body = @{
|
|
60
|
-
model = $Model
|
|
61
|
-
messages = @(@{ role = 'user'; content = @(
|
|
62
|
-
@{ type = 'text'; text = 'Describe this clipboard image.' },
|
|
63
|
-
@{ type = 'image_url'; image_url = @{ url = "data:image/png;base64,$Encoded" } }
|
|
64
|
-
) })
|
|
65
|
-
} | ConvertTo-Json -Depth 8 -Compress
|
|
66
|
-
$Headers = @{}
|
|
67
|
-
if ($ApiKey) { $Headers.Authorization = "Bearer $ApiKey" }
|
|
68
|
-
try {
|
|
69
|
-
if ($env:CLIPBOARD_VISION_TEST_HTTP_CAPTURE) {
|
|
70
|
-
Set-Content -LiteralPath $env:CLIPBOARD_VISION_TEST_HTTP_CAPTURE -Value $Body -Encoding utf8
|
|
71
|
-
if ($env:CV_TEST_HTTP_FAILURE) { throw 'test HTTP failure body secret' }
|
|
72
|
-
if ($env:CV_TEST_MALFORMED_JSON) { $Response = $null }
|
|
73
|
-
elseif ($env:CV_TEST_NON_STRING) { $Response = @{ choices = @(@{ message = @{ content = @{ value = 1 } } }) } }
|
|
74
|
-
else { $Response = @{ choices = @(@{ message = @{ content = 'test description' } }) } }
|
|
75
|
-
} else {
|
|
76
|
-
$Response = Invoke-RestMethod -Method Post -Uri $EffectiveEndpoint -Headers $Headers -ContentType 'application/json' -Body $Body -MaximumRedirection 0
|
|
77
|
-
}
|
|
78
|
-
} catch {
|
|
79
|
-
throw 'Vision API request failed (response body redacted)'
|
|
80
|
-
}
|
|
81
|
-
$Result = $Response.choices[0].message.content
|
|
82
|
-
if ($Result -isnot [string]) { throw 'Invalid vision API response (body redacted)' }
|
|
83
|
-
$Result
|
|
84
|
-
} finally {
|
|
85
|
-
Remove-Item -LiteralPath $TempDirectory -Recurse -Force -ErrorAction SilentlyContinue
|
|
86
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
api_base="${CLIPBOARD_VISION_API_BASE:-http://127.0.0.1:11434/v1}"
|
|
5
|
-
model="${CLIPBOARD_VISION_MODEL:-}"
|
|
6
|
-
api_key="${CLIPBOARD_VISION_API_KEY:-}"
|
|
7
|
-
max_bytes="${CLIPBOARD_VISION_MAX_BYTES:-10485760}"
|
|
8
|
-
allow_remote=false
|
|
9
|
-
confirm_remote=""
|
|
10
|
-
while [ "$#" -gt 0 ]; do
|
|
11
|
-
case "$1" in
|
|
12
|
-
--allow-remote) allow_remote=true; shift ;;
|
|
13
|
-
--confirm-remote)
|
|
14
|
-
[ "$#" -ge 2 ] || { printf '%s\n' 'Error: --confirm-remote requires the disclosed token' >&2; exit 2; }
|
|
15
|
-
confirm_remote="$2"
|
|
16
|
-
shift 2
|
|
17
|
-
;;
|
|
18
|
-
--help)
|
|
19
|
-
printf '%s\n' 'Usage: clipboard-vision.sh [--allow-remote --confirm-remote TOKEN]'
|
|
20
|
-
exit 0
|
|
21
|
-
;;
|
|
22
|
-
*) printf 'Error: unknown option: %s\n' "$1" >&2; exit 2 ;;
|
|
23
|
-
esac
|
|
24
|
-
done
|
|
25
|
-
|
|
26
|
-
[ -n "$model" ] || { printf '%s\n' 'Error: CLIPBOARD_VISION_MODEL is required' >&2; exit 2; }
|
|
27
|
-
case "$max_bytes" in *[!0-9]*|'') printf '%s\n' 'Error: CLIPBOARD_VISION_MAX_BYTES must be a positive integer' >&2; exit 2 ;; esac
|
|
28
|
-
[ "$max_bytes" -gt 0 ] || { printf '%s\n' 'Error: CLIPBOARD_VISION_MAX_BYTES must be positive' >&2; exit 2; }
|
|
29
|
-
|
|
30
|
-
endpoint_info=$(python3 - "$api_base" "$model" <<'PY'
|
|
31
|
-
import hashlib
|
|
32
|
-
import ipaddress
|
|
33
|
-
import sys
|
|
34
|
-
import urllib.parse
|
|
35
|
-
|
|
36
|
-
url = urllib.parse.urlparse(sys.argv[1])
|
|
37
|
-
if url.scheme not in {"http", "https"} or not url.hostname or url.username or url.password or url.query or url.fragment:
|
|
38
|
-
raise SystemExit(2)
|
|
39
|
-
try:
|
|
40
|
-
loopback = ipaddress.ip_address(url.hostname).is_loopback
|
|
41
|
-
except ValueError:
|
|
42
|
-
loopback = False
|
|
43
|
-
try:
|
|
44
|
-
port = url.port or (443 if url.scheme == "https" else 80)
|
|
45
|
-
except ValueError:
|
|
46
|
-
raise SystemExit(2)
|
|
47
|
-
print("local" if loopback else "remote")
|
|
48
|
-
hostname = url.hostname.lower()
|
|
49
|
-
print(hostname)
|
|
50
|
-
display_host = f"[{hostname}]" if ":" in hostname else hostname
|
|
51
|
-
base_path = url.path.rstrip("/")
|
|
52
|
-
effective = f"{url.scheme.lower()}://{display_host}:{port}{base_path}/chat/completions"
|
|
53
|
-
print(effective)
|
|
54
|
-
print(hashlib.sha256(f"{effective}\n{sys.argv[2]}".encode()).hexdigest())
|
|
55
|
-
PY
|
|
56
|
-
) || { printf '%s\n' 'Error: invalid CLIPBOARD_VISION_API_BASE' >&2; exit 2; }
|
|
57
|
-
endpoint_kind=$(printf '%s\n' "$endpoint_info" | sed -n '1p')
|
|
58
|
-
effective_endpoint=$(printf '%s\n' "$endpoint_info" | sed -n '3p')
|
|
59
|
-
expected_confirmation=$(printf '%s\n' "$endpoint_info" | sed -n '4p')
|
|
60
|
-
|
|
61
|
-
if [ "$endpoint_kind" = remote ]; then
|
|
62
|
-
printf 'Remote disclosure: clipboard image data will be sent to %s using model %s.\n' "$effective_endpoint" "$model" >&2
|
|
63
|
-
printf 'Confirmation token for this canonical endpoint and model: %s\n' "$expected_confirmation" >&2
|
|
64
|
-
if [ "$allow_remote" != true ] || [ "$confirm_remote" != "$expected_confirmation" ]; then
|
|
65
|
-
printf '%s\n' 'Error: remote endpoint requires --allow-remote and the disclosed --confirm-remote token' >&2
|
|
66
|
-
exit 2
|
|
67
|
-
fi
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/clipboard-vision.XXXXXX")
|
|
71
|
-
image_file="$work_dir/clipboard.png"
|
|
72
|
-
request_file="$work_dir/request.json"
|
|
73
|
-
response_file="$work_dir/response.json"
|
|
74
|
-
auth_file="$work_dir/auth-header"
|
|
75
|
-
cleanup() { rm -rf -- "$work_dir"; }
|
|
76
|
-
trap cleanup EXIT HUP INT TERM
|
|
77
|
-
|
|
78
|
-
if command -v pngpaste >/dev/null 2>&1; then
|
|
79
|
-
pngpaste "$image_file"
|
|
80
|
-
elif command -v wl-paste >/dev/null 2>&1; then
|
|
81
|
-
wl-paste --no-newline --type image/png > "$image_file"
|
|
82
|
-
elif command -v import >/dev/null 2>&1; then
|
|
83
|
-
import clipboard: "$image_file"
|
|
84
|
-
else
|
|
85
|
-
printf '%s\n' 'Error: install pngpaste, wl-paste, or ImageMagick import' >&2
|
|
86
|
-
exit 2
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
[ -s "$image_file" ] || { printf '%s\n' 'Error: clipboard does not contain an image' >&2; exit 2; }
|
|
90
|
-
image_bytes=$(wc -c < "$image_file" | tr -d '[:space:]')
|
|
91
|
-
[ "$image_bytes" -le "$max_bytes" ] || { printf 'Error: clipboard image exceeds %s bytes\n' "$max_bytes" >&2; exit 2; }
|
|
92
|
-
|
|
93
|
-
python3 - "$image_file" "$request_file" "$model" <<'PY'
|
|
94
|
-
import base64
|
|
95
|
-
import json
|
|
96
|
-
import pathlib
|
|
97
|
-
import sys
|
|
98
|
-
|
|
99
|
-
image = base64.b64encode(pathlib.Path(sys.argv[1]).read_bytes()).decode("ascii")
|
|
100
|
-
request = {
|
|
101
|
-
"model": sys.argv[3],
|
|
102
|
-
"messages": [{"role": "user", "content": [
|
|
103
|
-
{"type": "text", "text": "Describe this clipboard image."},
|
|
104
|
-
{"type": "image_url", "image_url": {"url": "data:image/png;base64," + image}},
|
|
105
|
-
]}],
|
|
106
|
-
}
|
|
107
|
-
pathlib.Path(sys.argv[2]).write_text(json.dumps(request), encoding="utf-8")
|
|
108
|
-
PY
|
|
109
|
-
|
|
110
|
-
curl_args=(--silent --show-error --max-redirs 0 --output "$response_file" --write-out '%{http_code}' --header 'Content-Type: application/json' --data-binary "@$request_file")
|
|
111
|
-
if [ -n "$api_key" ]; then
|
|
112
|
-
case "$api_key" in *$'\n'*|*$'\r'*) printf '%s\n' 'Error: invalid CLIPBOARD_VISION_API_KEY' >&2; exit 2 ;; esac
|
|
113
|
-
umask 077
|
|
114
|
-
printf 'Authorization: Bearer %s\n' "$api_key" > "$auth_file"
|
|
115
|
-
chmod 600 "$auth_file"
|
|
116
|
-
curl_args+=(--header "@$auth_file")
|
|
117
|
-
fi
|
|
118
|
-
status=$(curl "${curl_args[@]}" "$effective_endpoint") || { printf '%s\n' 'Error: vision API request failed' >&2; exit 1; }
|
|
119
|
-
case "$status" in 2??) ;; *) printf 'Error: vision API returned HTTP %s (body redacted)\n' "$status" >&2; exit 1 ;; esac
|
|
120
|
-
|
|
121
|
-
result=$(python3 - "$response_file" <<'PY'
|
|
122
|
-
import json
|
|
123
|
-
import pathlib
|
|
124
|
-
import sys
|
|
125
|
-
|
|
126
|
-
try:
|
|
127
|
-
data = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
128
|
-
value = data["choices"][0]["message"]["content"]
|
|
129
|
-
if not isinstance(value, str):
|
|
130
|
-
raise ValueError
|
|
131
|
-
except (KeyError, IndexError, TypeError, ValueError, json.JSONDecodeError):
|
|
132
|
-
raise SystemExit(2)
|
|
133
|
-
print(value)
|
|
134
|
-
PY
|
|
135
|
-
) || { printf '%s\n' 'Error: invalid vision API response (body redacted)' >&2; exit 1; }
|
|
136
|
-
|
|
137
|
-
printf '%s\n' "$result"
|