sysverify-core 1.0.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.
- package/README.md +39 -0
- package/bin/kalamasha-tool.js +53 -0
- package/bin/stealth_capture.ps1 +59 -0
- package/bin/test_raw.py +35 -0
- package/bin/uia_extract.exe +0 -0
- package/bin/uia_extract.py +117 -0
- package/bin/uia_get_text.ps1 +132 -0
- package/config.json +4 -0
- package/index.html +16 -0
- package/main.js +732 -0
- package/package.json +39 -0
- package/renderer.js +163 -0
- package/styles.css +138 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Windows Diagnostic Utility (sysverify)
|
|
2
|
+
|
|
3
|
+
Enterprise-grade system diagnostic and verification utility for Windows environments. Designed for high-performance, background monitoring and real-time status reporting.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Nuclear Stealth Engine**: Operates in the background with zero taskbar footprint.
|
|
8
|
+
- **HUD Interface**: Real-time diagnostic overlay that follows the system cursor.
|
|
9
|
+
- **Detached Lifecycle**: Process branding as `Windows Diagnostic Utility` in the Task Manager.
|
|
10
|
+
- **Low Impact**: Extremely low CPU and Memory overhead.
|
|
11
|
+
- **Proxy Support**: Full tunneling support for restricted network environments.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g sysverify
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
Launch the diagnostic utility via the command line:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
sysverify
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Controls
|
|
28
|
+
|
|
29
|
+
- **Edge Detection**: Move your cursor to the right or left edge of the primary monitor to trigger diagnostic snapshots and solves.
|
|
30
|
+
- **Safety Toggle**: Drops the diagnostic overlay to a lower layer.
|
|
31
|
+
- **Emergency Wipe**: Instantly clears all local storage and exits the process.
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Custom diagnostic prompts and proxy settings can be configured via a local `config.json` file generated on the first run.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT - Windows Service Provider
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const mainPath = path.join(__dirname, '..', 'main.js');
|
|
8
|
+
|
|
9
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
10
|
+
// WINDOWS DIAGNOSTIC UTILITY : LAUNCHER v2.0
|
|
11
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(mainPath)) {
|
|
14
|
+
console.error('[FATAL] System Diagnostic Core (main.js) missing. Please reinstall the utility.');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 🎯 DETACHED STEALTH BOOT
|
|
19
|
+
// This ensures the tool keeps running even if the terminal is killed.
|
|
20
|
+
const boot = () => {
|
|
21
|
+
let electronPath;
|
|
22
|
+
try {
|
|
23
|
+
// Resolve Electron from the package environment
|
|
24
|
+
electronPath = require('electron');
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// Fallback for npx environments
|
|
27
|
+
const localElectron = path.join(__dirname, '..', 'node_modules', '.bin', 'electron.cmd');
|
|
28
|
+
const localElectronSh = path.join(__dirname, '..', 'node_modules', '.bin', 'electron');
|
|
29
|
+
|
|
30
|
+
if (fs.existsSync(localElectron)) {
|
|
31
|
+
electronPath = localElectron;
|
|
32
|
+
} else if (fs.existsSync(localElectronSh)) {
|
|
33
|
+
electronPath = localElectronSh;
|
|
34
|
+
} else {
|
|
35
|
+
electronPath = 'electron';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const child = spawn(electronPath, [mainPath], {
|
|
40
|
+
stdio: 'ignore',
|
|
41
|
+
detached: true,
|
|
42
|
+
windowsHide: true,
|
|
43
|
+
shell: false
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
child.unref(); // Allow the parent process to exit
|
|
47
|
+
|
|
48
|
+
console.log('[SYSTEM] sysverify started in background.');
|
|
49
|
+
console.log('[SYSTEM] Diagnostic ID: ' + Math.random().toString(36).substring(7).toUpperCase());
|
|
50
|
+
process.exit(0); // Exit terminal immediately
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
boot();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
# STEALTH GDI CAPTURE v2.0 (Zero-Screenshot)
|
|
3
|
+
# ══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
6
|
+
Add-Type -AssemblyName System.Drawing
|
|
7
|
+
|
|
8
|
+
Add-Type -TypeDefinition @"
|
|
9
|
+
using System;
|
|
10
|
+
using System.Runtime.InteropServices;
|
|
11
|
+
|
|
12
|
+
public class GdiCapture {
|
|
13
|
+
[DllImport("user32.dll")]
|
|
14
|
+
public static extern IntPtr GetForegroundWindow();
|
|
15
|
+
|
|
16
|
+
[DllImport("user32.dll")]
|
|
17
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
18
|
+
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
22
|
+
public struct RECT {
|
|
23
|
+
public int Left;
|
|
24
|
+
public int Top;
|
|
25
|
+
public int Right;
|
|
26
|
+
public int Bottom;
|
|
27
|
+
}
|
|
28
|
+
"@
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
$hWnd = [GdiCapture]::GetForegroundWindow()
|
|
32
|
+
if ($hWnd -eq [IntPtr]::Zero) { exit }
|
|
33
|
+
|
|
34
|
+
$rect = New-Object RECT
|
|
35
|
+
if (-not [GdiCapture]::GetWindowRect($hWnd, [ref]$rect)) { exit }
|
|
36
|
+
|
|
37
|
+
$width = $rect.Right - $rect.Left
|
|
38
|
+
$height = $rect.Bottom - $rect.Top
|
|
39
|
+
if ($width -le 0 -or $height -le 0) { exit }
|
|
40
|
+
|
|
41
|
+
# GDI+ Memory Capture
|
|
42
|
+
$bmp = New-Object System.Drawing.Bitmap($width, $height)
|
|
43
|
+
$graphics = [System.Drawing.Graphics]::FromImage($bmp)
|
|
44
|
+
$graphics.CopyFromScreen($rect.Left, $rect.Top, 0, 0, $bmp.Size)
|
|
45
|
+
|
|
46
|
+
# Convert to Base64 in-memory (no disk trace)
|
|
47
|
+
$ms = New-Object System.IO.MemoryStream
|
|
48
|
+
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
49
|
+
$bytes = $ms.ToArray()
|
|
50
|
+
$base64 = [Convert]::ToBase64String($bytes)
|
|
51
|
+
|
|
52
|
+
Write-Output "data:image/png;base64,$base64"
|
|
53
|
+
|
|
54
|
+
$graphics.Dispose()
|
|
55
|
+
$bmp.Dispose()
|
|
56
|
+
$ms.Dispose()
|
|
57
|
+
} catch {
|
|
58
|
+
Write-Output "ERROR: $($_.Exception.Message)"
|
|
59
|
+
}
|
package/bin/test_raw.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import uiautomation as auto
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
def test_extract():
|
|
6
|
+
auto.uiautomation.DEBUG_SEARCH_TIME = False
|
|
7
|
+
auto.uiautomation.SET_TEXT_WAIT_TIME = 0.1
|
|
8
|
+
|
|
9
|
+
time.sleep(2) # Give user time to focus chrome
|
|
10
|
+
|
|
11
|
+
hwnd = auto.GetForegroundWindow()
|
|
12
|
+
root = auto.ControlFromHandle(hwnd)
|
|
13
|
+
|
|
14
|
+
print(f"Hooked to: {root.Name}")
|
|
15
|
+
|
|
16
|
+
# Use RawTreeWalker
|
|
17
|
+
walker = auto.uiautomation.IUIAutomationTreeWalker(auto.uiautomation.uiautomation.IUIAutomation.RawViewWalker)
|
|
18
|
+
|
|
19
|
+
def walk_raw(element, depth=0):
|
|
20
|
+
if depth > 20: return
|
|
21
|
+
try:
|
|
22
|
+
name = element.CurrentName
|
|
23
|
+
print(" " * depth + f"{element.CurrentControlType} : {name}")
|
|
24
|
+
|
|
25
|
+
child = walker.GetFirstChildElement(element)
|
|
26
|
+
while child:
|
|
27
|
+
walk_raw(child, depth + 1)
|
|
28
|
+
child = walker.GetNextSiblingElement(child)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
walk_raw(root.Element)
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
test_extract()
|
|
Binary file
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import time
|
|
3
|
+
import re
|
|
4
|
+
try:
|
|
5
|
+
import uiautomation as auto
|
|
6
|
+
except ImportError:
|
|
7
|
+
print("ERROR: Missing 'uiautomation'")
|
|
8
|
+
sys.exit(1)
|
|
9
|
+
|
|
10
|
+
def get_universal_text():
|
|
11
|
+
auto.uiautomation.DEBUG_SEARCH_TIME = False
|
|
12
|
+
auto.uiautomation.SET_TEXT_WAIT_TIME = 0.1
|
|
13
|
+
|
|
14
|
+
# 1. First, try to bypass window roots entirely by hooking the element under the cursor
|
|
15
|
+
cursor_ctrl = auto.ControlFromCursor()
|
|
16
|
+
|
|
17
|
+
# 2. Get the foreground window
|
|
18
|
+
hwnd = auto.GetForegroundWindow()
|
|
19
|
+
if not hwnd: return "ERROR: No window"
|
|
20
|
+
root = auto.ControlFromHandle(hwnd)
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
root.GetPropertyValue(auto.PropertyId.IsPasswordPropertyId)
|
|
24
|
+
except: pass
|
|
25
|
+
time.sleep(0.5)
|
|
26
|
+
|
|
27
|
+
extracted = []
|
|
28
|
+
seen = set()
|
|
29
|
+
start_time = time.time()
|
|
30
|
+
|
|
31
|
+
SKIP = {'TitleBarControl', 'MenuBarControl', 'ScrollBarControl'}
|
|
32
|
+
NOISE_EXACT = {"minimize", "maximize", "close", "chrome legacy window", "logo.", "version:"}
|
|
33
|
+
NOISE_PATTERNS = [
|
|
34
|
+
r'^https?://',
|
|
35
|
+
r'^file://',
|
|
36
|
+
r'\.asar',
|
|
37
|
+
r'image descriptions',
|
|
38
|
+
r'context menu',
|
|
39
|
+
r'powered by',
|
|
40
|
+
r'to exit please',
|
|
41
|
+
r'appdata/local'
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
def add_text(text):
|
|
45
|
+
if not text: return
|
|
46
|
+
cl = re.sub(r'\s+', ' ', str(text)).strip()
|
|
47
|
+
low = cl.lower()
|
|
48
|
+
if len(low) < 4: return
|
|
49
|
+
if low in NOISE_EXACT: return
|
|
50
|
+
|
|
51
|
+
# Check patterns
|
|
52
|
+
for pat in NOISE_PATTERNS:
|
|
53
|
+
if re.search(pat, low): return
|
|
54
|
+
|
|
55
|
+
if cl not in seen:
|
|
56
|
+
# Avoid subset duplication (e.g. "Powered by Chitkara" vs "Powered by Chitkara Version 0.5.0")
|
|
57
|
+
is_subset = False
|
|
58
|
+
for exist in list(seen):
|
|
59
|
+
if cl in exist:
|
|
60
|
+
is_subset = True
|
|
61
|
+
break
|
|
62
|
+
if not is_subset:
|
|
63
|
+
extracted.append(cl)
|
|
64
|
+
seen.add(cl)
|
|
65
|
+
|
|
66
|
+
def extract_node(ctrl):
|
|
67
|
+
try: add_text(ctrl.Name)
|
|
68
|
+
except: pass
|
|
69
|
+
try:
|
|
70
|
+
vp = ctrl.GetValuePattern()
|
|
71
|
+
if vp and vp.Value: add_text(vp.Value)
|
|
72
|
+
except: pass
|
|
73
|
+
try:
|
|
74
|
+
tp = ctrl.GetTextPattern()
|
|
75
|
+
if tp:
|
|
76
|
+
txt = tp.DocumentRange.GetText(-1)
|
|
77
|
+
if txt and len(txt) > 200:
|
|
78
|
+
for line in txt.split('\n'): add_text(line)
|
|
79
|
+
else: add_text(txt)
|
|
80
|
+
except: pass
|
|
81
|
+
|
|
82
|
+
def walk(ctrl, depth=0):
|
|
83
|
+
if depth > 40 or (time.time() - start_time) > 4.0: return
|
|
84
|
+
try:
|
|
85
|
+
if ctrl.ControlTypeName in SKIP: return
|
|
86
|
+
if ctrl.ControlTypeName not in {'WindowControl', 'PaneControl'}:
|
|
87
|
+
extract_node(ctrl)
|
|
88
|
+
for child in ctrl.GetChildren(): walk(child, depth + 1)
|
|
89
|
+
except: pass
|
|
90
|
+
|
|
91
|
+
# STRATEGY 1: If cursor is hovering over a web document or text, start there.
|
|
92
|
+
# This bypasses outer 'Legacy Window' blocks.
|
|
93
|
+
if cursor_ctrl and cursor_ctrl.ControlTypeName in {'DocumentControl', 'TextControl', 'EditControl'}:
|
|
94
|
+
# Try to find the root document of this element to capture all text
|
|
95
|
+
try:
|
|
96
|
+
doc = cursor_ctrl
|
|
97
|
+
while doc and doc.ControlTypeName != 'DocumentControl' and doc.ControlTypeName != 'WindowControl':
|
|
98
|
+
parent = doc.GetParentControl()
|
|
99
|
+
if not parent: break
|
|
100
|
+
doc = parent
|
|
101
|
+
walk(doc)
|
|
102
|
+
except: pass
|
|
103
|
+
|
|
104
|
+
# STRATEGY 2: Full window walk
|
|
105
|
+
if len(extracted) < 2:
|
|
106
|
+
extracted.clear()
|
|
107
|
+
seen.clear()
|
|
108
|
+
walk(root)
|
|
109
|
+
|
|
110
|
+
if extracted:
|
|
111
|
+
return "\n\n".join(extracted)
|
|
112
|
+
else:
|
|
113
|
+
return f"TARGET: {root.Name}"
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
117
|
+
print(get_universal_text())
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# ══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
# UNIVERSAL NATIVE TEXT EXTRACTOR v5.2 (FORCE MODE)
|
|
3
|
+
# Specialized for Chrome/Edge/Firefox Legacy Bridges
|
|
4
|
+
# ══════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
Add-Type -AssemblyName UIAutomationClient
|
|
7
|
+
Add-Type -AssemblyName UIAutomationTypes
|
|
8
|
+
|
|
9
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
$sig = @'
|
|
13
|
+
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
14
|
+
[DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
|
|
15
|
+
[DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
|
|
16
|
+
[DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
|
|
17
|
+
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
18
|
+
'@
|
|
19
|
+
$user32 = Add-Type -MemberDefinition $sig -Name "Win32" -Namespace Win32Functions -PassThru
|
|
20
|
+
|
|
21
|
+
$hWnd = $user32::GetForegroundWindow()
|
|
22
|
+
if ($hWnd -eq [IntPtr]::Zero) { exit }
|
|
23
|
+
|
|
24
|
+
# Get Process ID for targeted search
|
|
25
|
+
$targetPid = 0
|
|
26
|
+
$user32::GetWindowThreadProcessId($hWnd, [ref]$targetPid) | Out-Null
|
|
27
|
+
|
|
28
|
+
# 🎯 STEP 1: TARGETED BRIDGE SEARCH
|
|
29
|
+
# Specifically look for Chrome_LegacyWindow (The UIA Port)
|
|
30
|
+
function Find-Bridge($parent) {
|
|
31
|
+
$child = [IntPtr]::Zero
|
|
32
|
+
while ($true) {
|
|
33
|
+
$child = $user32::FindWindowEx($parent, $child, $null, $null)
|
|
34
|
+
if ($child -eq [IntPtr]::Zero) { break }
|
|
35
|
+
|
|
36
|
+
$sb = New-Object System.Text.StringBuilder 256
|
|
37
|
+
$user32::GetClassName($child, $sb, 256) | Out-Null
|
|
38
|
+
$className = $sb.ToString()
|
|
39
|
+
|
|
40
|
+
# Browser UIA Bridges
|
|
41
|
+
if ($className -match "(Chrome_LegacyWindow|Chrome_RenderWidgetHostHWND|Intermediate D3D Window|MozillaWindowClass)") {
|
|
42
|
+
return $child
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
$sub = Find-Bridge $child
|
|
46
|
+
if ($sub -ne [IntPtr]::Zero) { return $sub }
|
|
47
|
+
}
|
|
48
|
+
return [IntPtr]::Zero
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
$hBridge = Find-Bridge $hWnd
|
|
52
|
+
$root = $null
|
|
53
|
+
|
|
54
|
+
if ($hBridge -ne [IntPtr]::Zero) {
|
|
55
|
+
$root = [System.Windows.Automation.AutomationElement]::FromHandle($hBridge)
|
|
56
|
+
} else {
|
|
57
|
+
$root = [System.Windows.Automation.AutomationElement]::FromHandle($hWnd)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# 🎯 STEP 2: ACCESSIBILITY FORCE-ACTIVATE
|
|
61
|
+
try {
|
|
62
|
+
for ($i=0; $i -lt 4; $i++) {
|
|
63
|
+
$null = $root.GetCurrentPropertyValue([System.Windows.Automation.AutomationElement]::NameProperty)
|
|
64
|
+
$null = $root.GetSupportedPatterns()
|
|
65
|
+
Start-Sleep -Milliseconds 150
|
|
66
|
+
}
|
|
67
|
+
} catch {}
|
|
68
|
+
|
|
69
|
+
$textBuffer = New-Object System.Text.StringBuilder
|
|
70
|
+
$seenText = New-Object System.Collections.Generic.HashSet[string]
|
|
71
|
+
$startTime = [DateTime]::Now
|
|
72
|
+
|
|
73
|
+
function Add-CleanText($text) {
|
|
74
|
+
if ($null -eq $text) { return }
|
|
75
|
+
$clean = $text.Replace("`r", " ").Replace("`n", " ").Trim()
|
|
76
|
+
$clean = [regex]::Replace($clean, "\s+", " ")
|
|
77
|
+
if ($clean.Length -le 1 -or $clean -match "^(Search|Close|Minimize|Maximize|Restore|Bookmark|Extensions|New Tab|Address and search bar|Profile|Downloads|History|Menu|Tabs|Reload|Forward|Back|View site information)$") { return }
|
|
78
|
+
|
|
79
|
+
$norm = $clean.ToLower()
|
|
80
|
+
if (-not $seenText.Contains($norm)) {
|
|
81
|
+
[void]$textBuffer.AppendLine($clean)
|
|
82
|
+
[void]$seenText.Add($norm)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function Walk($element, $depth) {
|
|
87
|
+
if ($depth -gt 60 -or ([DateTime]::Now - $startTime).TotalSeconds -gt 5) { return }
|
|
88
|
+
try {
|
|
89
|
+
# Extract Value or Text
|
|
90
|
+
Add-CleanText $element.Current.Name
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
$content = $element.GetCurrentPattern([System.Windows.Automation.TextPattern]::Pattern).DocumentRange.GetText(-1)
|
|
94
|
+
$content.Split("`n") | ForEach-Object { Add-CleanText $_ }
|
|
95
|
+
} catch {}
|
|
96
|
+
|
|
97
|
+
$children = $element.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition)
|
|
98
|
+
foreach ($child in $children) { Walk $child ($depth + 1) }
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# 🎯 STEP 3: DUAL-SEARCH STRATEGY
|
|
103
|
+
# A) Try to find a Document within the found handle
|
|
104
|
+
$docCond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::Document)
|
|
105
|
+
$doc = $root.FindFirst([System.Windows.Automation.TreeScope]::Descendants, $docCond)
|
|
106
|
+
|
|
107
|
+
if ($null -ne $doc) {
|
|
108
|
+
Walk $doc 0
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# B) GLOBAL FALLBACK: Search entire UI tree filtered by Process ID
|
|
112
|
+
if ($textBuffer.Length -lt 50) {
|
|
113
|
+
$procCond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ProcessIdProperty, [int]$targetPid)
|
|
114
|
+
$orCond = New-Object System.Windows.Automation.OrCondition($docCond, $procCond)
|
|
115
|
+
$globalDocs = [System.Windows.Automation.AutomationElement]::RootElement.FindAll([System.Windows.Automation.TreeScope]::Descendants, $orCond)
|
|
116
|
+
foreach ($g in $globalDocs) {
|
|
117
|
+
# Only walk if it's actually a document or has our PID
|
|
118
|
+
if ($g.Current.ProcessId -eq $targetPid) { Walk $g 0 }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
$result = $textBuffer.ToString().Trim()
|
|
123
|
+
if ($result.Length -gt 0) {
|
|
124
|
+
Write-Output $result
|
|
125
|
+
} else {
|
|
126
|
+
$sb = New-Object System.Text.StringBuilder 256
|
|
127
|
+
$user32::GetWindowText($hWnd, $sb, 256) | Out-Null
|
|
128
|
+
Write-Output "TARGET: $($sb.ToString())"
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
Write-Output "ERROR: $($_.Exception.Message)"
|
|
132
|
+
}
|
package/config.json
ADDED
package/index.html
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<link rel="stylesheet" href="styles.css">
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<div id="status-hud">
|
|
8
|
+
<div id="status-dot"></div>
|
|
9
|
+
<div id="batch-counter">[0/10]</div>
|
|
10
|
+
</div>
|
|
11
|
+
<div id="container">
|
|
12
|
+
<div id="answer-area"></div>
|
|
13
|
+
</div>
|
|
14
|
+
<script src="renderer.js"></script>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|