@jshookmcp/jshook 0.1.5 → 0.1.6

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 (81) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +72 -40
  3. package/README.zh.md +77 -40
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.js +13 -1
  6. package/dist/modules/analyzer/IntelligentAnalyzer.js +19 -11
  7. package/dist/modules/browser/BrowserModeManager.d.ts +5 -0
  8. package/dist/modules/browser/BrowserModeManager.js +96 -10
  9. package/dist/modules/browser/CamoufoxBrowserManager.d.ts +4 -0
  10. package/dist/modules/browser/CamoufoxBrowserManager.js +64 -3
  11. package/dist/modules/browser/TabRegistry.js +3 -2
  12. package/dist/modules/browser/UnifiedBrowserManager.d.ts +5 -0
  13. package/dist/modules/browser/UnifiedBrowserManager.js +62 -9
  14. package/dist/modules/captcha/AICaptchaDetector.js +185 -185
  15. package/dist/modules/debugger/DebuggerSessionManager.d.ts +4 -0
  16. package/dist/modules/debugger/DebuggerSessionManager.js +29 -19
  17. package/dist/modules/debugger/ScriptManager.impl.class.d.ts +4 -0
  18. package/dist/modules/debugger/ScriptManager.impl.class.js +46 -21
  19. package/dist/modules/emulator/EnvironmentEmulator.js +2 -2
  20. package/dist/modules/monitor/NetworkMonitor.impl.d.ts +1 -0
  21. package/dist/modules/monitor/NetworkMonitor.impl.js +22 -15
  22. package/dist/modules/monitor/PerformanceMonitor.js +64 -32
  23. package/dist/modules/process/LinuxProcessManager.d.ts +3 -1
  24. package/dist/modules/process/LinuxProcessManager.js +7 -3
  25. package/dist/modules/process/MacProcessManager.d.ts +3 -1
  26. package/dist/modules/process/MacProcessManager.js +32 -28
  27. package/dist/modules/process/ProcessManager.impl.d.ts +5 -1
  28. package/dist/modules/process/ProcessManager.impl.js +54 -13
  29. package/dist/modules/process/index.d.ts +3 -1
  30. package/dist/modules/process/index.js +2 -2
  31. package/dist/modules/process/memory/AuditTrail.d.ts +25 -0
  32. package/dist/modules/process/memory/AuditTrail.js +44 -0
  33. package/dist/modules/process/memory/availability.js +49 -49
  34. package/dist/modules/process/memory/injector.js +185 -185
  35. package/dist/modules/process/memory/linux/mapsParser.d.ts +16 -0
  36. package/dist/modules/process/memory/linux/mapsParser.js +28 -0
  37. package/dist/modules/process/memory/reader.js +50 -50
  38. package/dist/modules/process/memory/regions.enumerate.js +45 -1
  39. package/dist/modules/process/memory/regions.protection.js +48 -2
  40. package/dist/modules/process/memory/scanner.d.ts +4 -1
  41. package/dist/modules/process/memory/scanner.js +383 -182
  42. package/dist/modules/process/memory/writer.js +54 -54
  43. package/dist/native/NativeMemoryManager.impl.d.ts +4 -0
  44. package/dist/native/NativeMemoryManager.impl.js +72 -24
  45. package/dist/native/NativeMemoryManager.utils.d.ts +1 -0
  46. package/dist/native/NativeMemoryManager.utils.js +44 -1
  47. package/dist/native/scripts/linux/enum-windows.sh +12 -12
  48. package/dist/native/scripts/macos/enum-windows.applescript +22 -22
  49. package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  50. package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
  51. package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
  52. package/dist/server/MCPServer.search.d.ts +3 -0
  53. package/dist/server/MCPServer.search.js +21 -2
  54. package/dist/server/ToolCallContextGuard.d.ts +2 -0
  55. package/dist/server/ToolCallContextGuard.js +29 -14
  56. package/dist/server/ToolSearch.js +11 -5
  57. package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
  58. package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
  59. package/dist/server/domains/browser/definitions.tools.security.js +76 -76
  60. package/dist/server/domains/browser/handlers/tab-workflow.js +6 -4
  61. package/dist/server/domains/maintenance/handlers.extensions.js +46 -26
  62. package/dist/server/domains/process/definitions.js +20 -7
  63. package/dist/server/domains/process/handlers.impl.core.runtime.base.d.ts +35 -0
  64. package/dist/server/domains/process/handlers.impl.core.runtime.base.js +107 -1
  65. package/dist/server/domains/process/handlers.impl.core.runtime.inject.js +111 -2
  66. package/dist/server/domains/process/handlers.impl.core.runtime.memory.d.ts +9 -0
  67. package/dist/server/domains/process/handlers.impl.core.runtime.memory.js +282 -31
  68. package/dist/server/domains/process/manifest.js +1 -0
  69. package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
  70. package/dist/server/domains/workflow/handlers.impl.workflow-api.js +14 -4
  71. package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
  72. package/dist/server/registry/discovery.js +17 -12
  73. package/dist/server/registry/index.js +10 -2
  74. package/dist/utils/TokenBudgetManager.d.ts +1 -0
  75. package/dist/utils/TokenBudgetManager.js +22 -0
  76. package/package.json +5 -1
  77. package/src/native/scripts/linux/enum-windows.sh +12 -12
  78. package/src/native/scripts/macos/enum-windows.applescript +22 -22
  79. package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  80. package/src/native/scripts/windows/enum-windows.ps1 +44 -44
  81. package/src/native/scripts/windows/inject-dll.ps1 +21 -21
@@ -55,6 +55,7 @@ export class NetworkMonitor {
55
55
  responses = new Map();
56
56
  MAX_NETWORK_RECORDS = 500;
57
57
  MAX_INJECTED_RECORDS = 500;
58
+ JS_RESPONSE_CONCURRENCY = 6;
58
59
  responseBodyCache = new Map();
59
60
  MAX_BODY_CACHE_ENTRIES = 200;
60
61
  networkListeners = {};
@@ -313,24 +314,30 @@ export class NetworkMonitor {
313
314
  }
314
315
  }
315
316
  async getAllJavaScriptResponses() {
316
- const jsResponses = [];
317
- for (const [requestId, response] of this.responses.entries()) {
318
- if (response.mimeType.includes('javascript') ||
317
+ const candidates = Array.from(this.responses.entries()).filter(([, response]) => {
318
+ return (response.mimeType.includes('javascript') ||
319
319
  response.url.endsWith('.js') ||
320
- response.url.includes('.js?')) {
320
+ response.url.includes('.js?'));
321
+ });
322
+ const jsResponses = [];
323
+ for (let i = 0; i < candidates.length; i += this.JS_RESPONSE_CONCURRENCY) {
324
+ const batch = candidates.slice(i, i + this.JS_RESPONSE_CONCURRENCY);
325
+ const batchResults = await Promise.all(batch.map(async ([requestId, response]) => {
321
326
  const bodyResult = await this.getResponseBody(requestId);
322
- if (bodyResult) {
323
- const content = bodyResult.base64Encoded
324
- ? Buffer.from(bodyResult.body, 'base64').toString('utf-8')
325
- : bodyResult.body;
326
- jsResponses.push({
327
- url: response.url,
328
- content,
329
- size: content.length,
330
- requestId,
331
- });
327
+ if (!bodyResult) {
328
+ return null;
332
329
  }
333
- }
330
+ const content = bodyResult.base64Encoded
331
+ ? Buffer.from(bodyResult.body, 'base64').toString('utf-8')
332
+ : bodyResult.body;
333
+ return {
334
+ url: response.url,
335
+ content,
336
+ size: content.length,
337
+ requestId,
338
+ };
339
+ }));
340
+ jsResponses.push(...batchResults.filter((value) => value !== null));
334
341
  }
335
342
  logger.info(`Collected ${jsResponses.length} JavaScript responses`);
336
343
  return jsResponses;
@@ -1,4 +1,5 @@
1
1
  import { writeFile } from 'node:fs/promises';
2
+ import { setImmediate as waitForImmediate } from 'node:timers/promises';
2
3
  import { logger } from '../../utils/logger.js';
3
4
  import { PrerequisiteError } from '../../errors/PrerequisiteError.js';
4
5
  import { cdpLimit } from '../../utils/concurrency.js';
@@ -65,6 +66,62 @@ function isCDPHeapSamplingNode(value) {
65
66
  function isCDPHeapSamplingPayload(value) {
66
67
  return isRecord(value) && isRecord(value.profile) && isCDPHeapSamplingNode(value.profile.head);
67
68
  }
69
+ async function yieldToEventLoop() {
70
+ await waitForImmediate();
71
+ }
72
+ function countTraceEvents(traceData) {
73
+ const eventPattern = /"ph"\s*:/g;
74
+ let count = 0;
75
+ while (eventPattern.exec(traceData) !== null) {
76
+ count++;
77
+ }
78
+ return count;
79
+ }
80
+ function insertTopAllocation(topAllocations, candidate, topN) {
81
+ if (topN <= 0) {
82
+ return;
83
+ }
84
+ if (topAllocations.length === topN &&
85
+ candidate.selfSize <= topAllocations[topAllocations.length - 1].selfSize) {
86
+ return;
87
+ }
88
+ let insertIndex = topAllocations.findIndex((entry) => candidate.selfSize > entry.selfSize);
89
+ if (insertIndex === -1) {
90
+ insertIndex = topAllocations.length;
91
+ }
92
+ topAllocations.splice(insertIndex, 0, candidate);
93
+ if (topAllocations.length > topN) {
94
+ topAllocations.length = topN;
95
+ }
96
+ }
97
+ function collectTopHeapAllocations(root, topN) {
98
+ const stack = [root];
99
+ const topAllocations = [];
100
+ let sampleCount = 0;
101
+ while (stack.length > 0) {
102
+ const node = stack.pop();
103
+ if (!node) {
104
+ continue;
105
+ }
106
+ if (node.callFrame) {
107
+ sampleCount++;
108
+ insertTopAllocation(topAllocations, {
109
+ functionName: node.callFrame.functionName || '(anonymous)',
110
+ url: node.callFrame.url || '',
111
+ selfSize: node.selfSize || 0,
112
+ }, topN);
113
+ }
114
+ if (Array.isArray(node.children)) {
115
+ for (let i = node.children.length - 1; i >= 0; i--) {
116
+ const child = node.children[i];
117
+ if (child) {
118
+ stack.push(child);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return { sampleCount, topAllocations };
124
+ }
68
125
  export class PerformanceMonitor {
69
126
  collector;
70
127
  cdpSession = null;
@@ -306,14 +363,7 @@ export class PerformanceMonitor {
306
363
  traceData = traceChunks.join('');
307
364
  }
308
365
  this.tracingEnabled = false;
309
- let eventCount = 0;
310
- try {
311
- const parsed = JSON.parse(traceData);
312
- eventCount = Array.isArray(parsed) ? parsed.length : (parsed.traceEvents?.length ?? 0);
313
- }
314
- catch {
315
- eventCount = (traceData.match(/"ph":/g) || []).length;
316
- }
366
+ const eventCount = countTraceEvents(traceData);
317
367
  let savedPath;
318
368
  if (options?.artifactPath) {
319
369
  await writeFile(options.artifactPath, traceData, 'utf-8');
@@ -364,28 +414,10 @@ export class PerformanceMonitor {
364
414
  await cdp.send('HeapProfiler.disable');
365
415
  this.heapSamplingEnabled = false;
366
416
  const topN = options?.topN ?? 20;
367
- const allNodes = [];
368
- function walkNodes(node) {
369
- if (!isCDPHeapSamplingNode(node)) {
370
- return;
371
- }
372
- if (node.callFrame) {
373
- allNodes.push({
374
- functionName: node.callFrame.functionName || '(anonymous)',
375
- url: node.callFrame.url || '',
376
- selfSize: node.selfSize || 0,
377
- });
378
- }
379
- if (node.children) {
380
- for (const child of node.children) {
381
- walkNodes(child);
382
- }
383
- }
384
- }
385
- walkNodes(profile.head);
386
- allNodes.sort((a, b) => b.selfSize - a.selfSize);
387
- const topAllocations = allNodes.slice(0, topN);
388
- const profileJson = JSON.stringify(profile, null, 2);
417
+ await yieldToEventLoop();
418
+ const { sampleCount, topAllocations } = collectTopHeapAllocations(profile.head, topN);
419
+ await yieldToEventLoop();
420
+ const profileJson = JSON.stringify(profile);
389
421
  let savedPath;
390
422
  if (options?.artifactPath) {
391
423
  await writeFile(options.artifactPath, profileJson, 'utf-8');
@@ -400,10 +432,10 @@ export class PerformanceMonitor {
400
432
  await writeFile(absolutePath, profileJson, 'utf-8');
401
433
  savedPath = displayPath;
402
434
  }
403
- logger.success('Heap sampling profile saved', { sampleCount: allNodes.length, path: savedPath });
435
+ logger.success('Heap sampling profile saved', { sampleCount, path: savedPath });
404
436
  return {
405
437
  artifactPath: savedPath,
406
- sampleCount: allNodes.length,
438
+ sampleCount,
407
439
  topAllocations,
408
440
  };
409
441
  });
@@ -19,7 +19,9 @@ export declare class LinuxProcessManager {
19
19
  commandLine?: string;
20
20
  parentPid?: number;
21
21
  }>;
22
- checkDebugPort(pid: number): Promise<number | null>;
22
+ checkDebugPort(pid: number, options?: {
23
+ commandLine?: string;
24
+ }): Promise<number | null>;
23
25
  launchWithDebug(executablePath: string, debugPort?: number, args?: string[]): Promise<ProcessInfo | null>;
24
26
  killProcess(pid: number): Promise<boolean>;
25
27
  isRunningOnWayland(): boolean;
@@ -212,10 +212,10 @@ export class LinuxProcessManager {
212
212
  return {};
213
213
  }
214
214
  }
215
- async checkDebugPort(pid) {
215
+ async checkDebugPort(pid, options) {
216
216
  try {
217
217
  pid = safePid(pid);
218
- const { commandLine } = await this.getProcessCommandLine(pid);
218
+ const commandLine = options?.commandLine ?? (await this.getProcessCommandLine(pid)).commandLine;
219
219
  if (commandLine) {
220
220
  const match = commandLine.match(/--remote-debugging-port=(\d+)/);
221
221
  if (match && match[1]) {
@@ -244,7 +244,11 @@ export class LinuxProcessManager {
244
244
  });
245
245
  child.unref();
246
246
  await new Promise(resolve => setTimeout(resolve, PROCESS_LAUNCH_WAIT_MS));
247
- const process = await this.getProcessByPid(child.pid || 0);
247
+ if (!child.pid) {
248
+ logger.error(`Failed to spawn process: PID is undefined for ${executablePath}`);
249
+ return null;
250
+ }
251
+ const process = await this.getProcessByPid(child.pid);
248
252
  logger.info(`Launched process with debug port ${debugPort}:`, {
249
253
  pid: child.pid,
250
254
  executable: executablePath,
@@ -18,7 +18,9 @@ export declare class MacProcessManager {
18
18
  commandLine?: string;
19
19
  parentPid?: number;
20
20
  }>;
21
- checkDebugPort(pid: number): Promise<number | null>;
21
+ checkDebugPort(pid: number, options?: {
22
+ commandLine?: string;
23
+ }): Promise<number | null>;
22
24
  launchWithDebug(executablePath: string, debugPort?: number, args?: string[]): Promise<ProcessInfo | null>;
23
25
  killProcess(pid: number): Promise<boolean>;
24
26
  }
@@ -103,31 +103,31 @@ export class MacProcessManager {
103
103
  if (!process) {
104
104
  return [];
105
105
  }
106
- const appleScript = `
107
- tell application "System Events"
108
- set processList to {}
109
- try
110
- set targetProcess to first process whose unix id is ${pid}
111
- set procName to name of targetProcess
112
- set windowList to {}
113
-
114
- tell targetProcess
115
- repeat with win in windows
116
- set winInfo to {|
117
- title:name of win,
118
- className:procName,
119
- processId:${pid},
120
- handle:"applescript-window"
121
- |}
122
- set end of windowList to winInfo
123
- end repeat
124
- end tell
125
-
126
- return windowList
127
- on error
128
- return {}
129
- end try
130
- end tell
106
+ const appleScript = `
107
+ tell application "System Events"
108
+ set processList to {}
109
+ try
110
+ set targetProcess to first process whose unix id is ${pid}
111
+ set procName to name of targetProcess
112
+ set windowList to {}
113
+
114
+ tell targetProcess
115
+ repeat with win in windows
116
+ set winInfo to {|
117
+ title:name of win,
118
+ className:procName,
119
+ processId:${pid},
120
+ handle:"applescript-window"
121
+ |}
122
+ set end of windowList to winInfo
123
+ end repeat
124
+ end tell
125
+
126
+ return windowList
127
+ on error
128
+ return {}
129
+ end try
130
+ end tell
131
131
  `;
132
132
  const { stdout } = await execAsync(`osascript -e '${appleScript.replace(/'/g, "'\"'\"'")}' 2>/dev/null || echo "[]"`, { timeout: 5000 });
133
133
  const windows = [];
@@ -261,10 +261,10 @@ export class MacProcessManager {
261
261
  return {};
262
262
  }
263
263
  }
264
- async checkDebugPort(pid) {
264
+ async checkDebugPort(pid, options) {
265
265
  try {
266
266
  pid = safePid(pid);
267
- const { commandLine } = await this.getProcessCommandLine(pid);
267
+ const commandLine = options?.commandLine ?? (await this.getProcessCommandLine(pid)).commandLine;
268
268
  if (commandLine) {
269
269
  const match = commandLine.match(/--remote-debugging-port=(\d+)/);
270
270
  if (match && match[1]) {
@@ -293,7 +293,11 @@ export class MacProcessManager {
293
293
  });
294
294
  child.unref();
295
295
  await new Promise(resolve => setTimeout(resolve, PROCESS_LAUNCH_WAIT_MS));
296
- const process = await this.getProcessByPid(child.pid || 0);
296
+ if (!child.pid) {
297
+ logger.error(`Failed to spawn process: PID is undefined for ${executablePath}`);
298
+ return null;
299
+ }
300
+ const process = await this.getProcessByPid(child.pid);
297
301
  logger.info(`Launched process with debug port ${debugPort}:`, {
298
302
  pid: child.pid,
299
303
  executable: executablePath,
@@ -5,8 +5,10 @@ export declare class ProcessManager {
5
5
  private powershellPath;
6
6
  private scriptLoader;
7
7
  private browserDiscovery;
8
+ private processCache;
8
9
  constructor();
9
10
  findProcesses(pattern: string): Promise<ProcessInfo[]>;
11
+ private computeProcessDiff;
10
12
  getProcessByPid(pid: number): Promise<ProcessInfo | null>;
11
13
  getProcessWindows(pid: number): Promise<WindowInfo[]>;
12
14
  findChromiumProcesses(config?: TargetAppConfig): Promise<ChromiumProcess>;
@@ -15,7 +17,9 @@ export declare class ProcessManager {
15
17
  commandLine?: string;
16
18
  parentPid?: number;
17
19
  }>;
18
- checkDebugPort(pid: number): Promise<number | null>;
20
+ checkDebugPort(pid: number, options?: {
21
+ commandLine?: string;
22
+ }): Promise<number | null>;
19
23
  private findPidByListeningPort;
20
24
  launchWithDebug(executablePath: string, debugPort?: number, args?: string[]): Promise<ProcessInfo | null>;
21
25
  injectDll(_pid: number, _dllPath: string): Promise<boolean>;
@@ -8,6 +8,7 @@ import { findChromiumProcessesWithConfig } from '../process/ProcessManager.chrom
8
8
  import { DEFAULT_CHROMIUM_CONFIG, } from '../process/ProcessManager.types.js';
9
9
  export { DEFAULT_CHROMIUM_CONFIG, } from '../process/ProcessManager.types.js';
10
10
  const execAsync = promisify(exec);
11
+ const PROCESS_SNAPSHOT_CACHE_TTL_MS = 3000;
11
12
  function sanitizePsPattern(s) {
12
13
  return String(s || '').replace(/[`$"'{}();|<>@#%!\\\n\r]/g, '');
13
14
  }
@@ -21,6 +22,7 @@ export class ProcessManager {
21
22
  powershellPath = 'powershell.exe';
22
23
  scriptLoader;
23
24
  browserDiscovery;
25
+ processCache = new Map();
24
26
  constructor() {
25
27
  this.scriptLoader = new ScriptLoader();
26
28
  this.browserDiscovery = new BrowserDiscovery();
@@ -29,6 +31,12 @@ export class ProcessManager {
29
31
  async findProcesses(pattern) {
30
32
  try {
31
33
  const normalizedPattern = sanitizePsPattern(String(pattern || '').trim());
34
+ const cacheKey = normalizedPattern.toLowerCase() || '*';
35
+ const now = Date.now();
36
+ const cachedEntry = this.processCache.get(cacheKey);
37
+ if (cachedEntry && cachedEntry.expiresAt > now) {
38
+ return cachedEntry.snapshot;
39
+ }
32
40
  let psCommand;
33
41
  if (normalizedPattern) {
34
42
  psCommand = `Get-Process -Name "*${normalizedPattern.replace(/"/g, '""')}*" -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, Path, MainWindowTitle, MainWindowHandle, CPU, WorkingSet64 | ConvertTo-Json -Compress`;
@@ -37,20 +45,30 @@ export class ProcessManager {
37
45
  psCommand = `Get-Process -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, Path, MainWindowTitle, MainWindowHandle, CPU, WorkingSet64 | ConvertTo-Json -Compress`;
38
46
  }
39
47
  const { stdout } = await execAsync(`${this.powershellPath} -NoProfile -Command "${psCommand}"`, { maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES });
40
- const processes = [];
41
48
  const lines = stdout.trim();
42
- if (!lines || lines === 'null' || lines === '') {
43
- return processes;
49
+ const processes = [];
50
+ if (lines && lines !== 'null') {
51
+ const data = JSON.parse(lines);
52
+ const procList = Array.isArray(data) ? data : [data];
53
+ for (const proc of procList) {
54
+ processes.push({
55
+ pid: proc.Id,
56
+ name: proc.ProcessName,
57
+ executablePath: proc.Path,
58
+ });
59
+ }
44
60
  }
45
- const data = JSON.parse(lines);
46
- const procList = Array.isArray(data) ? data : [data];
47
- for (const proc of procList) {
48
- processes.push({
49
- pid: proc.Id,
50
- name: proc.ProcessName,
51
- executablePath: proc.Path,
52
- });
61
+ const byPid = new Map();
62
+ for (const process of processes) {
63
+ byPid.set(process.pid, process);
53
64
  }
65
+ const lastDelta = this.computeProcessDiff(cachedEntry?.byPid ?? new Map(), byPid);
66
+ this.processCache.set(cacheKey, {
67
+ expiresAt: now + PROCESS_SNAPSHOT_CACHE_TTL_MS,
68
+ snapshot: processes,
69
+ byPid,
70
+ lastDelta,
71
+ });
54
72
  const patternStr = normalizedPattern.length > 0 ? `'${normalizedPattern}'` : 'all';
55
73
  logger.info(`Found ${processes.length} processes matching ${patternStr}`);
56
74
  return processes;
@@ -60,6 +78,29 @@ export class ProcessManager {
60
78
  return [];
61
79
  }
62
80
  }
81
+ computeProcessDiff(previousByPid, nextByPid) {
82
+ const added = [];
83
+ const removed = [];
84
+ const changed = [];
85
+ for (const [pid, nextProcess] of nextByPid) {
86
+ const previousProcess = previousByPid.get(pid);
87
+ if (!previousProcess) {
88
+ added.push(nextProcess);
89
+ continue;
90
+ }
91
+ if (previousProcess.pid !== nextProcess.pid ||
92
+ previousProcess.name !== nextProcess.name ||
93
+ previousProcess.executablePath !== nextProcess.executablePath) {
94
+ changed.push({ before: previousProcess, after: nextProcess });
95
+ }
96
+ }
97
+ for (const [pid, previousProcess] of previousByPid) {
98
+ if (!nextByPid.has(pid)) {
99
+ removed.push(previousProcess);
100
+ }
101
+ }
102
+ return { added, removed, changed };
103
+ }
63
104
  async getProcessByPid(pid) {
64
105
  try {
65
106
  pid = safePid(pid);
@@ -142,10 +183,10 @@ export class ProcessManager {
142
183
  return {};
143
184
  }
144
185
  }
145
- async checkDebugPort(pid) {
186
+ async checkDebugPort(pid, options) {
146
187
  try {
147
188
  pid = safePid(pid);
148
- const { commandLine } = await this.getProcessCommandLine(pid);
189
+ const commandLine = options?.commandLine ?? (await this.getProcessCommandLine(pid)).commandLine;
149
190
  if (commandLine) {
150
191
  const match = commandLine.match(/--remote-debugging-port=(\d+)/);
151
192
  if (match && match[1]) {
@@ -22,7 +22,9 @@ export declare class UnifiedProcessManager {
22
22
  findProcesses(pattern: string): Promise<import("../process/ProcessManager.js").ProcessInfo[]>;
23
23
  getProcessByPid(pid: number): Promise<import("../process/ProcessManager.js").ProcessInfo | null>;
24
24
  getProcessWindows(pid: number): Promise<import("../process/ProcessManager.js").WindowInfo[]>;
25
- checkDebugPort(pid: number): Promise<number | null>;
25
+ checkDebugPort(pid: number, options?: {
26
+ commandLine?: string;
27
+ }): Promise<number | null>;
26
28
  launchWithDebug(executablePath: string, debugPort?: number, args?: string[]): Promise<import("../process/ProcessManager.js").ProcessInfo | null>;
27
29
  killProcess(pid: number): Promise<boolean>;
28
30
  getProcessCommandLine(pid: number): Promise<{
@@ -58,8 +58,8 @@ export class UnifiedProcessManager {
58
58
  async getProcessWindows(pid) {
59
59
  return this.manager.getProcessWindows(pid);
60
60
  }
61
- async checkDebugPort(pid) {
62
- return this.manager.checkDebugPort(pid);
61
+ async checkDebugPort(pid, options) {
62
+ return this.manager.checkDebugPort(pid, options);
63
63
  }
64
64
  async launchWithDebug(executablePath, debugPort, args) {
65
65
  return this.manager.launchWithDebug(executablePath, debugPort, args);
@@ -0,0 +1,25 @@
1
+ export interface AuditEntry {
2
+ timestamp: string;
3
+ operation: string;
4
+ pid: number | null;
5
+ address: string | null;
6
+ size: number | null;
7
+ result: 'success' | 'failure';
8
+ error?: string;
9
+ durationMs: number;
10
+ user: string;
11
+ pattern?: string;
12
+ resultsCount?: number;
13
+ dllPath?: string;
14
+ }
15
+ export declare class MemoryAuditTrail {
16
+ private buffer;
17
+ private head;
18
+ private count;
19
+ private readonly capacity;
20
+ constructor(capacity?: number);
21
+ record(entry: Omit<AuditEntry, 'timestamp' | 'user'>): void;
22
+ exportJson(): string;
23
+ clear(): void;
24
+ size(): number;
25
+ }
@@ -0,0 +1,44 @@
1
+ export class MemoryAuditTrail {
2
+ buffer;
3
+ head = 0;
4
+ count = 0;
5
+ capacity;
6
+ constructor(capacity = 5000) {
7
+ this.capacity = Number.isInteger(capacity) && capacity > 0 ? capacity : 5000;
8
+ this.buffer = [];
9
+ }
10
+ record(entry) {
11
+ const fullEntry = {
12
+ ...entry,
13
+ timestamp: new Date().toISOString(),
14
+ user: process.env.USERNAME || process.env.USER || 'unknown',
15
+ };
16
+ if (this.count < this.capacity) {
17
+ const writeIndex = (this.head + this.count) % this.capacity;
18
+ this.buffer[writeIndex] = fullEntry;
19
+ this.count += 1;
20
+ return;
21
+ }
22
+ this.buffer[this.head] = fullEntry;
23
+ this.head = (this.head + 1) % this.capacity;
24
+ }
25
+ exportJson() {
26
+ const entries = [];
27
+ for (let i = 0; i < this.count; i += 1) {
28
+ const index = (this.head + i) % this.capacity;
29
+ const entry = this.buffer[index];
30
+ if (entry) {
31
+ entries.push(entry);
32
+ }
33
+ }
34
+ return JSON.stringify(entries, null, 2);
35
+ }
36
+ clear() {
37
+ this.buffer = [];
38
+ this.head = 0;
39
+ this.count = 0;
40
+ }
41
+ size() {
42
+ return this.count;
43
+ }
44
+ }
@@ -103,55 +103,55 @@ export async function checkDebugPort(platform, pid) {
103
103
  return { success: false, error: 'Debug port check currently only implemented for Windows' };
104
104
  }
105
105
  try {
106
- const psScript = `
107
- Add-Type @"
108
- using System;
109
- using System.Runtime.InteropServices;
110
- using System.ComponentModel;
111
-
112
- public class DebugChecker {
113
- [DllImport("ntdll.dll")]
114
- public static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, out IntPtr processInformation, int processInformationLength, out int returnLength);
115
-
116
- [DllImport("kernel32.dll", SetLastError = true)]
117
- public static extern IntPtr OpenProcess(int access, bool inherit, int pid);
118
-
119
- [DllImport("kernel32.dll", SetLastError = true)]
120
- public static extern bool CloseHandle(IntPtr handle);
121
-
122
- const int PROCESS_QUERY_INFORMATION = 0x0400;
123
- const int ProcessDebugPort = 7;
124
-
125
- public static object Check(int pid) {
126
- IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
127
- if (hProcess == IntPtr.Zero) {
128
- int error = Marshal.GetLastWin32Error();
129
- throw new Win32Exception(error, "Failed to open process");
130
- }
131
-
132
- try {
133
- IntPtr debugPort;
134
- int returnLength;
135
- int status = NtQueryInformationProcess(hProcess, ProcessDebugPort, out debugPort, IntPtr.Size, out returnLength);
136
-
137
- if (status != 0) {
138
- return new { success = false, error = "NtQueryInformationProcess failed with status: 0x" + status.ToString("X") };
139
- }
140
-
141
- return new { success = true, isDebugged = debugPort != IntPtr.Zero };
142
- } finally {
143
- CloseHandle(hProcess);
144
- }
145
- }
146
- }
147
- "@
148
-
149
- try {
150
- $result = [DebugChecker]::Check(${pid})
151
- $result | ConvertTo-Json -Compress
152
- } catch {
153
- @{ success = $false; error = $_.Exception.Message } | ConvertTo-Json -Compress
154
- }
106
+ const psScript = `
107
+ Add-Type @"
108
+ using System;
109
+ using System.Runtime.InteropServices;
110
+ using System.ComponentModel;
111
+
112
+ public class DebugChecker {
113
+ [DllImport("ntdll.dll")]
114
+ public static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, out IntPtr processInformation, int processInformationLength, out int returnLength);
115
+
116
+ [DllImport("kernel32.dll", SetLastError = true)]
117
+ public static extern IntPtr OpenProcess(int access, bool inherit, int pid);
118
+
119
+ [DllImport("kernel32.dll", SetLastError = true)]
120
+ public static extern bool CloseHandle(IntPtr handle);
121
+
122
+ const int PROCESS_QUERY_INFORMATION = 0x0400;
123
+ const int ProcessDebugPort = 7;
124
+
125
+ public static object Check(int pid) {
126
+ IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
127
+ if (hProcess == IntPtr.Zero) {
128
+ int error = Marshal.GetLastWin32Error();
129
+ throw new Win32Exception(error, "Failed to open process");
130
+ }
131
+
132
+ try {
133
+ IntPtr debugPort;
134
+ int returnLength;
135
+ int status = NtQueryInformationProcess(hProcess, ProcessDebugPort, out debugPort, IntPtr.Size, out returnLength);
136
+
137
+ if (status != 0) {
138
+ return new { success = false, error = "NtQueryInformationProcess failed with status: 0x" + status.ToString("X") };
139
+ }
140
+
141
+ return new { success = true, isDebugged = debugPort != IntPtr.Zero };
142
+ } finally {
143
+ CloseHandle(hProcess);
144
+ }
145
+ }
146
+ }
147
+ "@
148
+
149
+ try {
150
+ $result = [DebugChecker]::Check(${pid})
151
+ $result | ConvertTo-Json -Compress
152
+ } catch {
153
+ @{ success = $false; error = $_.Exception.Message } | ConvertTo-Json -Compress
154
+ }
155
155
  `;
156
156
  const { stdout } = await executePowerShellScript(psScript, { maxBuffer: 1024 * 1024, timeout: 10000 });
157
157
  const _trimmed = stdout.trim();