fchek 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.
Files changed (66) hide show
  1. package/README.md +64 -0
  2. package/bin/fchek.js +107 -0
  3. package/lib/api.js +110 -0
  4. package/lib/audit.js +211 -0
  5. package/lib/bench.js +248 -0
  6. package/lib/config.js +191 -0
  7. package/lib/context.js +356 -0
  8. package/lib/convention.js +526 -0
  9. package/lib/coverage.js +604 -0
  10. package/lib/db.js +135 -0
  11. package/lib/deps-check.js +264 -0
  12. package/lib/deps.js +374 -0
  13. package/lib/docker.js +84 -0
  14. package/lib/doctor.js +149 -0
  15. package/lib/dom.js +226 -0
  16. package/lib/fuzz.js +470 -0
  17. package/lib/git.js +290 -0
  18. package/lib/goto.js +544 -0
  19. package/lib/launch.js +182 -0
  20. package/lib/lint.js +624 -0
  21. package/lib/new_features.test.js +181 -0
  22. package/lib/output.js +46 -0
  23. package/lib/port.js +173 -0
  24. package/lib/process.js +228 -0
  25. package/lib/profile.js +453 -0
  26. package/lib/python.js +41 -0
  27. package/lib/race.js +186 -0
  28. package/lib/registry.js +179 -0
  29. package/lib/repl.js +135 -0
  30. package/lib/run.js +403 -0
  31. package/lib/screenshot.js +152 -0
  32. package/lib/secrets.js +257 -0
  33. package/lib/state.js +219 -0
  34. package/lib/test.js +471 -0
  35. package/lib/vuln.js +253 -0
  36. package/lib/watch.js +240 -0
  37. package/lib/winlog.js +123 -0
  38. package/package.json +27 -0
  39. package/skills/ACTIVATE.md +274 -0
  40. package/skills/README.md +163 -0
  41. package/skills/agent.md +444 -0
  42. package/skills/api.md +47 -0
  43. package/skills/bench.md +117 -0
  44. package/skills/context.md +116 -0
  45. package/skills/convention.md +143 -0
  46. package/skills/coverage.md +99 -0
  47. package/skills/csharp.md +97 -0
  48. package/skills/db.md +66 -0
  49. package/skills/deps-check.md +135 -0
  50. package/skills/deps.md +143 -0
  51. package/skills/docker.md +61 -0
  52. package/skills/dom.md +56 -0
  53. package/skills/fuzz.md +167 -0
  54. package/skills/goto.md +111 -0
  55. package/skills/lint.md +123 -0
  56. package/skills/port.md +57 -0
  57. package/skills/profile.md +91 -0
  58. package/skills/race.md +117 -0
  59. package/skills/repl.md +81 -0
  60. package/skills/rules.md +318 -0
  61. package/skills/run.md +135 -0
  62. package/skills/secrets.md +170 -0
  63. package/skills/security.md +360 -0
  64. package/skills/state.md +261 -0
  65. package/skills/vuln.md +57 -0
  66. package/skills/windows.md +320 -0
package/lib/launch.js ADDED
@@ -0,0 +1,182 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * launch.js — start an application and wait for its window to appear
5
+ *
6
+ * Solves the problem: "did the app actually open and is the UI showing?"
7
+ * Returns PID, window title, position when the window is ready.
8
+ * Agent can then take a screenshot to verify UI state.
9
+ */
10
+
11
+ const { spawnSync, spawn } = require('child_process');
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const { output, ok, fail } = require('./output');
15
+
16
+ const HELP = `
17
+ fchek launch <exe> [--wait=<ms>] [--title=<partial>] [--args=<args>] [--kill-existing]
18
+
19
+ Launch an application and wait for its window to appear.
20
+ Use before fchek screenshot to verify the app started correctly.
21
+
22
+ Options:
23
+ --wait=5000 Max wait time for window in ms (default: 10000)
24
+ --title=<text> Window title to wait for (default: exe basename)
25
+ --args=<args> Arguments to pass to the executable
26
+ --kill-existing Kill existing instances before launching
27
+
28
+ Examples:
29
+ fchek launch bin/Vertex.exe
30
+ fchek launch bin/Vertex.exe --title=SpotlightWindow --wait=8000
31
+ fchek launch "C:/App/App.exe" --kill-existing
32
+ `.trim();
33
+
34
+ function runPowerShell(script, timeoutMs = 20000) {
35
+ return spawnSync('powershell', [
36
+ '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script,
37
+ ], { encoding: 'utf8', timeout: timeoutMs, windowsHide: false });
38
+ }
39
+
40
+ async function run(args) {
41
+ if (args.length === 0 || args[0] === '--help') { console.log(HELP); return; }
42
+
43
+ if (process.platform !== 'win32') {
44
+ return output(fail('fchek launch is Windows-only.'));
45
+ }
46
+
47
+ const exePath = args[0];
48
+ const waitMs = parseInt((args.find(a => a.startsWith('--wait=')) || '--wait=10000').replace('--wait=', ''), 10);
49
+ const titleHint = (args.find(a => a.startsWith('--title=')) || '').replace('--title=', '') ||
50
+ path.basename(exePath, path.extname(exePath));
51
+ const exeArgs = (args.find(a => a.startsWith('--args=')) || '').replace('--args=', '') || '';
52
+ const killExisting = args.includes('--kill-existing');
53
+
54
+ if (!fs.existsSync(exePath)) {
55
+ return output(fail(`Executable not found: ${exePath}`));
56
+ }
57
+
58
+ const absExe = path.resolve(exePath);
59
+
60
+ const script = `
61
+ Add-Type @"
62
+ using System;
63
+ using System.Runtime.InteropServices;
64
+ using System.Drawing;
65
+ public class WinCheck {
66
+ [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hwnd, out RECT rect);
67
+ [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hwnd);
68
+ public struct RECT { public int Left, Top, Right, Bottom; }
69
+ }
70
+ "@
71
+
72
+ ${killExisting ? `
73
+ # Kill existing instances
74
+ $exeName = [System.IO.Path]::GetFileNameWithoutExtension(${JSON.stringify(absExe)})
75
+ Get-Process -Name $exeName -ErrorAction SilentlyContinue | Stop-Process -Force
76
+ Start-Sleep -Milliseconds 500
77
+ ` : ''}
78
+
79
+ # Start the process
80
+ $psi = New-Object System.Diagnostics.ProcessStartInfo
81
+ $psi.FileName = ${JSON.stringify(absExe)}
82
+ $psi.Arguments = ${JSON.stringify(exeArgs)}
83
+ $psi.UseShellExecute = $true
84
+ $proc = [System.Diagnostics.Process]::Start($psi)
85
+
86
+ if ($null -eq $proc) {
87
+ Write-Output '{"launched":false,"error":"Failed to start process"}'
88
+ exit 0
89
+ }
90
+
91
+ $pid = $proc.Id
92
+ $waitMs = ${waitMs}
93
+ $step = 200
94
+ $elapsed = 0
95
+ $windowTitle = ""
96
+ $windowFound = $false
97
+
98
+ while ($elapsed -lt $waitMs) {
99
+ Start-Sleep -Milliseconds $step
100
+ $elapsed += $step
101
+
102
+ try { $proc.Refresh() } catch {}
103
+
104
+ # Check if process is still running
105
+ if ($proc.HasExited) {
106
+ Write-Output ('{"launched":true,"pid":' + $pid + ',"window_appeared":false,"error":"Process exited with code ' + $proc.ExitCode + '","elapsed_ms":' + $elapsed + '}')
107
+ exit 0
108
+ }
109
+
110
+ # Check for window
111
+ try {
112
+ $hwnd = $proc.MainWindowHandle
113
+ if ($hwnd -ne [IntPtr]::Zero) {
114
+ $title = $proc.MainWindowTitle
115
+ if ([string]::IsNullOrEmpty(${JSON.stringify(titleHint)}) -or $title -match [regex]::Escape(${JSON.stringify(titleHint)})) {
116
+ $rect = New-Object WinCheck+RECT
117
+ [WinCheck]::GetWindowRect($hwnd, [ref]$rect) | Out-Null
118
+ $w = $rect.Right - $rect.Left
119
+ $h = $rect.Bottom - $rect.Top
120
+ $windowTitle = $title
121
+ $windowFound = $true
122
+ Write-Output ('{"launched":true,"pid":' + $pid + ',"window_appeared":true,"window_title":"' + $title.Replace('"','\"') + '","x":' + $rect.Left + ',"y":' + $rect.Top + ',"width":' + $w + ',"height":' + $h + ',"elapsed_ms":' + $elapsed + '}')
123
+ exit 0
124
+ }
125
+ }
126
+ } catch {}
127
+
128
+ # Also search all processes for matching window title
129
+ $matching = Get-Process -ErrorAction SilentlyContinue | Where-Object {
130
+ $_.MainWindowTitle -match [regex]::Escape(${JSON.stringify(titleHint)}) -and $_.MainWindowHandle -ne [IntPtr]::Zero
131
+ }
132
+ if ($matching) {
133
+ $m = $matching[0]
134
+ $rect = New-Object WinCheck+RECT
135
+ [WinCheck]::GetWindowRect($m.MainWindowHandle, [ref]$rect) | Out-Null
136
+ $w = $rect.Right - $rect.Left
137
+ $h = $rect.Bottom - $rect.Top
138
+ Write-Output ('{"launched":true,"pid":' + $m.Id + ',"window_appeared":true,"window_title":"' + $m.MainWindowTitle.Replace('"','\"') + '","x":' + $rect.Left + ',"y":' + $rect.Top + ',"width":' + $w + ',"height":' + $h + ',"elapsed_ms":' + $elapsed + '}')
139
+ exit 0
140
+ }
141
+ }
142
+
143
+ # Timed out — window didn't appear
144
+ Write-Output ('{"launched":true,"pid":' + $pid + ',"window_appeared":false,"error":"Window did not appear within ' + $waitMs + 'ms","elapsed_ms":' + $elapsed + '}')
145
+ `;
146
+
147
+ const res = runPowerShell(script, waitMs + 10000);
148
+
149
+ if (res.error) {
150
+ return output(fail(`PowerShell error: ${res.error.message}`));
151
+ }
152
+
153
+ const jsonLine = (res.stdout || '').split('\n').map(l => l.trim()).find(l => l.startsWith('{'));
154
+
155
+ if (!jsonLine) {
156
+ return output(fail(`launch output error: ${(res.stderr || res.stdout || '').slice(0, 500)}`));
157
+ }
158
+
159
+ let data;
160
+ try { data = JSON.parse(jsonLine); }
161
+ catch { return output(fail(`parse error: ${jsonLine.slice(0, 200)}`)); }
162
+
163
+ if (data.error && !data.launched) {
164
+ return output(fail(data.error));
165
+ }
166
+
167
+ output(ok({
168
+ exe: absExe,
169
+ launched: data.launched,
170
+ pid: data.pid,
171
+ window_appeared: data.window_appeared,
172
+ window_title: data.window_title || null,
173
+ position: data.x != null ? { x: data.x, y: data.y, width: data.width, height: data.height } : null,
174
+ elapsed_ms: data.elapsed_ms,
175
+ error: data.error || null,
176
+ next_step: data.window_appeared
177
+ ? `Window is visible. Run: fchek screenshot --window="${data.window_title || titleHint}"`
178
+ : 'Window did not appear. Check fchek winlog for errors.',
179
+ }));
180
+ }
181
+
182
+ module.exports = { run };