@jshookmcp/jshook 0.1.6 → 0.1.8

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 (65) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +145 -100
  3. package/README.zh.md +81 -36
  4. package/dist/constants.d.ts +1 -1
  5. package/dist/constants.js +3 -1
  6. package/dist/index.js +0 -0
  7. package/dist/modules/analyzer/QualityAnalyzer.js +1 -1
  8. package/dist/modules/browser/BrowserDiscovery.js +2 -2
  9. package/dist/modules/browser/BrowserModeManager.js +3 -3
  10. package/dist/modules/captcha/AICaptchaDetector.d.ts +12 -16
  11. package/dist/modules/captcha/AICaptchaDetector.js +229 -209
  12. package/dist/modules/captcha/CaptchaDetector.constants.d.ts +2 -0
  13. package/dist/modules/captcha/CaptchaDetector.constants.js +116 -25
  14. package/dist/modules/captcha/CaptchaDetector.d.ts +2 -11
  15. package/dist/modules/captcha/CaptchaDetector.js +102 -51
  16. package/dist/modules/captcha/types.d.ts +46 -0
  17. package/dist/modules/captcha/types.js +52 -0
  18. package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +15 -20
  19. package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +66 -234
  20. package/dist/modules/deobfuscator/Deobfuscator.d.ts +3 -10
  21. package/dist/modules/deobfuscator/Deobfuscator.js +125 -404
  22. package/dist/modules/deobfuscator/webcrack.d.ts +13 -0
  23. package/dist/modules/deobfuscator/webcrack.js +164 -0
  24. package/dist/modules/detector/ObfuscationDetector.d.ts +6 -0
  25. package/dist/modules/detector/ObfuscationDetector.js +53 -2
  26. package/dist/modules/hook/AIHookGenerator.js +1 -1
  27. package/dist/modules/process/MacProcessManager.js +25 -25
  28. package/dist/modules/process/memory/availability.js +49 -49
  29. package/dist/modules/process/memory/injector.js +185 -185
  30. package/dist/modules/process/memory/reader.js +50 -50
  31. package/dist/modules/process/memory/scanner.js +165 -165
  32. package/dist/modules/process/memory/writer.js +55 -55
  33. package/dist/native/scripts/linux/enum-windows.sh +12 -12
  34. package/dist/native/scripts/macos/enum-windows.applescript +22 -22
  35. package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  36. package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
  37. package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
  38. package/dist/server/domains/analysis/definitions.js +223 -2
  39. package/dist/server/domains/analysis/handlers.impl.d.ts +2 -3
  40. package/dist/server/domains/analysis/handlers.impl.js +60 -15
  41. package/dist/server/domains/analysis/manifest.js +2 -5
  42. package/dist/server/domains/browser/definitions.tools.behavior.js +36 -24
  43. package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
  44. package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
  45. package/dist/server/domains/browser/definitions.tools.security.js +80 -77
  46. package/dist/server/domains/browser/handlers/camoufox-flow.js +0 -1
  47. package/dist/server/domains/browser/handlers/captcha-solver.d.ts +1 -1
  48. package/dist/server/domains/browser/handlers/captcha-solver.js +121 -54
  49. package/dist/server/domains/browser/handlers/page-navigation.js +0 -2
  50. package/dist/server/domains/browser/handlers.impl.d.ts +1 -1
  51. package/dist/server/domains/browser/handlers.impl.js +3 -3
  52. package/dist/server/domains/browser/manifest.js +1 -1
  53. package/dist/server/domains/shared/modules.d.ts +1 -0
  54. package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
  55. package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
  56. package/dist/types/deobfuscator.d.ts +43 -1
  57. package/dist/types/index.d.ts +1 -1
  58. package/dist/utils/config.js +19 -10
  59. package/package.json +30 -44
  60. package/scripts/postinstall.cjs +37 -0
  61. package/src/native/scripts/linux/enum-windows.sh +12 -12
  62. package/src/native/scripts/macos/enum-windows.applescript +22 -22
  63. package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  64. package/src/native/scripts/windows/enum-windows.ps1 +44 -44
  65. package/src/native/scripts/windows/inject-dll.ps1 +21 -21
@@ -164,69 +164,69 @@ export class WorkflowHandlersBase {
164
164
  initBuiltinScripts() {
165
165
  this.scriptRegistry.set('auth_extract', {
166
166
  description: 'Extract auth tokens from localStorage and cookies',
167
- code: `(function(){
168
- var keys=['token','active_token','access_token','jwt','auth_token','userRole','id_token','refresh_token'];
169
- var r={};
170
- for(var i=0;i<keys.length;i++){var v=localStorage.getItem(keys[i]);if(v)r[keys[i]]=v;}
171
- r._cookies=document.cookie;
172
- return r;
167
+ code: `(function(){
168
+ var keys=['token','active_token','access_token','jwt','auth_token','userRole','id_token','refresh_token'];
169
+ var r={};
170
+ for(var i=0;i<keys.length;i++){var v=localStorage.getItem(keys[i]);if(v)r[keys[i]]=v;}
171
+ r._cookies=document.cookie;
172
+ return r;
173
173
  })()`,
174
174
  });
175
175
  this.scriptRegistry.set('bundle_search', {
176
176
  description: 'Fetch a remote JS bundle and search it with regex patterns. params: { url: string, patterns: string[] }',
177
- code: `(async function(){
178
- var p=typeof __params__!=='undefined'?__params__:{};
179
- if(!p.url)return{error:'params.url required'};
180
- var resp=await fetch(p.url);
181
- var text=await resp.text();
182
- var patterns=p.patterns||[];
183
- var results={};
184
- for(var i=0;i<patterns.length;i++){
185
- var re=new RegExp(patterns[i],'g');
186
- var matches=[];var m;
187
- while((m=re.exec(text))!==null){
188
- var s=Math.max(0,m.index-80),e=Math.min(text.length,m.index+m[0].length+80);
189
- matches.push({match:m[0],ctx:text.slice(s,e)});
190
- if(matches.length>=10)break;
191
- }
192
- results[patterns[i]]=matches;
193
- }
194
- return{size:text.length,results:results};
177
+ code: `(async function(){
178
+ var p=typeof __params__!=='undefined'?__params__:{};
179
+ if(!p.url)return{error:'params.url required'};
180
+ var resp=await fetch(p.url);
181
+ var text=await resp.text();
182
+ var patterns=p.patterns||[];
183
+ var results={};
184
+ for(var i=0;i<patterns.length;i++){
185
+ var re=new RegExp(patterns[i],'g');
186
+ var matches=[];var m;
187
+ while((m=re.exec(text))!==null){
188
+ var s=Math.max(0,m.index-80),e=Math.min(text.length,m.index+m[0].length+80);
189
+ matches.push({match:m[0],ctx:text.slice(s,e)});
190
+ if(matches.length>=10)break;
191
+ }
192
+ results[patterns[i]]=matches;
193
+ }
194
+ return{size:text.length,results:results};
195
195
  })()`,
196
196
  });
197
197
  this.scriptRegistry.set('react_fill_form', {
198
198
  description: 'Fill React controlled form inputs using native setter trick. params: { fields: { "selector": "value" } }',
199
- code: `(function(){
200
- var p=typeof __params__!=='undefined'?__params__:{};
201
- var fields=p.fields||{};
202
- var ns=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set;
203
- var r={};
204
- var entries=Object.entries(fields);
205
- for(var i=0;i<entries.length;i++){
206
- var sel=entries[i][0],val=entries[i][1];
207
- var el=document.querySelector(sel);
208
- if(!el){r[sel]='not found';continue;}
209
- ns.call(el,val);
210
- el.dispatchEvent(new Event('input',{bubbles:true}));
211
- el.dispatchEvent(new Event('change',{bubbles:true}));
212
- r[sel]='filled';
213
- }
214
- return r;
199
+ code: `(function(){
200
+ var p=typeof __params__!=='undefined'?__params__:{};
201
+ var fields=p.fields||{};
202
+ var ns=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set;
203
+ var r={};
204
+ var entries=Object.entries(fields);
205
+ for(var i=0;i<entries.length;i++){
206
+ var sel=entries[i][0],val=entries[i][1];
207
+ var el=document.querySelector(sel);
208
+ if(!el){r[sel]='not found';continue;}
209
+ ns.call(el,val);
210
+ el.dispatchEvent(new Event('input',{bubbles:true}));
211
+ el.dispatchEvent(new Event('change',{bubbles:true}));
212
+ r[sel]='filled';
213
+ }
214
+ return r;
215
215
  })()`,
216
216
  });
217
217
  this.scriptRegistry.set('dom_find_upgrade_buttons', {
218
218
  description: 'Scan the current page for upgrade/subscription/tier-related UI elements',
219
- code: `(function(){
220
- var kw=['upgrade','plus','pro','premium','subscribe','plan','tier','vip','membership'];
221
- var r=[];
222
- document.querySelectorAll('button,a,[role=button],[class*=upgrade],[class*=premium],[class*=plus]').forEach(function(el){
223
- var t=(el.textContent||'').toLowerCase().trim();
224
- var c=(el.className||'').toLowerCase();
225
- if(kw.some(function(k){return t.includes(k)||c.includes(k);})){
226
- r.push({tag:el.tagName,text:t.slice(0,120),cls:c.slice(0,100),href:el.href||null,id:el.id||null});
227
- }
228
- });
229
- return r;
219
+ code: `(function(){
220
+ var kw=['upgrade','plus','pro','premium','subscribe','plan','tier','vip','membership'];
221
+ var r=[];
222
+ document.querySelectorAll('button,a,[role=button],[class*=upgrade],[class*=premium],[class*=plus]').forEach(function(el){
223
+ var t=(el.textContent||'').toLowerCase().trim();
224
+ var c=(el.className||'').toLowerCase();
225
+ if(kw.some(function(k){return t.includes(k)||c.includes(k);})){
226
+ r.push({tag:el.tagName,text:t.slice(0,120),cls:c.slice(0,100),href:el.href||null,id:el.id||null});
227
+ }
228
+ });
229
+ return r;
230
230
  })()`,
231
231
  });
232
232
  }
@@ -1,4 +1,4 @@
1
- export type ObfuscationType = 'javascript-obfuscator' | 'webpack' | 'uglify' | 'vm-protection' | 'self-modifying' | 'invisible-unicode' | 'control-flow-flattening' | 'string-array-rotation' | 'dead-code-injection' | 'opaque-predicates' | 'jsfuck' | 'aaencode' | 'jjencode' | 'packer' | 'eval-obfuscation' | 'base64-encoding' | 'hex-encoding' | 'jscrambler' | 'urlencoded' | 'custom' | 'unknown';
1
+ export type ObfuscationType = 'javascript-obfuscator' | 'webpack' | 'uglify' | 'vm-protection' | 'self-modifying' | 'invisible-unicode' | 'control-flow-flattening' | 'string-array-rotation' | 'dead-code-injection' | 'opaque-predicates' | 'jsfuck' | 'aaencode' | 'jjencode' | 'packer' | 'eval-obfuscation' | 'base64-encoding' | 'hex-encoding' | 'jscrambler' | 'urlencoded' | 'custom' | 'unknown' | 'bundle-unpack' | 'unminify' | 'jsx-decompile' | 'mangle' | 'webcrack';
2
2
  export interface Transformation {
3
3
  type: string;
4
4
  description: string;
@@ -11,6 +11,42 @@ export interface DeobfuscateOptions {
11
11
  preserveLogic?: boolean;
12
12
  renameVariables?: boolean;
13
13
  inlineFunctions?: boolean;
14
+ unpack?: boolean;
15
+ unminify?: boolean;
16
+ jsx?: boolean;
17
+ mangle?: boolean;
18
+ outputDir?: string;
19
+ forceOutput?: boolean;
20
+ includeModuleCode?: boolean;
21
+ maxBundleModules?: number;
22
+ mappings?: DeobfuscateMappingRule[];
23
+ }
24
+ export interface DeobfuscateMappingRule {
25
+ path: string;
26
+ pattern: string;
27
+ matchType?: 'includes' | 'regex' | 'exact';
28
+ target?: 'code' | 'path';
29
+ }
30
+ export interface DeobfuscateSavedArtifact {
31
+ path: string;
32
+ size: number;
33
+ type: 'file';
34
+ }
35
+ export interface DeobfuscateBundleModuleSummary {
36
+ id: string;
37
+ path: string;
38
+ isEntry: boolean;
39
+ size: number;
40
+ code?: string;
41
+ mappedPathFrom?: string;
42
+ }
43
+ export interface DeobfuscateBundleSummary {
44
+ type: 'webpack' | 'browserify';
45
+ entryId: string;
46
+ moduleCount: number;
47
+ truncated: boolean;
48
+ mappingsApplied?: number;
49
+ modules: DeobfuscateBundleModuleSummary[];
14
50
  }
15
51
  export interface DeobfuscateResult {
16
52
  code: string;
@@ -19,4 +55,10 @@ export interface DeobfuscateResult {
19
55
  obfuscationType: ObfuscationType[];
20
56
  transformations: Transformation[];
21
57
  analysis: string;
58
+ bundle?: DeobfuscateBundleSummary;
59
+ savedTo?: string;
60
+ savedArtifacts?: DeobfuscateSavedArtifact[];
61
+ warnings?: string[];
62
+ engine?: 'legacy' | 'webcrack' | 'hybrid';
63
+ webcrackApplied?: boolean;
22
64
  }
@@ -2,7 +2,7 @@ export type { CodeLocation, Result } from './common.js';
2
2
  export type { Config, LLMConfig, PuppeteerConfig, MCPConfig, CacheConfig, PerformanceConfig, } from './config.js';
3
3
  export type { BrowserContext } from './browser.js';
4
4
  export type { CollectCodeOptions, CodeFile, CollectCodeResult, DependencyGraph, DependencyNode, DependencyEdge, } from './collector.js';
5
- export type { ObfuscationType, Transformation, DeobfuscateOptions, DeobfuscateResult, } from './deobfuscator.js';
5
+ export type { ObfuscationType, Transformation, DeobfuscateOptions, DeobfuscateResult, DeobfuscateMappingRule, DeobfuscateSavedArtifact, DeobfuscateBundleModuleSummary, DeobfuscateBundleSummary, } from './deobfuscator.js';
6
6
  export type { UnderstandCodeOptions, UnderstandCodeResult, CodeStructure, TechStack, BusinessLogic, DataFlow, DataFlowGraph, DataFlowNode, DataFlowEdge, DataSource, DataSink, TaintPath, SecurityRisk, FunctionInfo, ClassInfo, PropertyInfo, ModuleInfo, CallGraph, CallGraphNode, CallGraphEdge, } from './analysis.js';
7
7
  export type { DetectCryptoOptions, DetectCryptoResult, CryptoAlgorithm, CryptoParameters, CryptoLibrary, } from './crypto.js';
8
8
  export type { HookOptions, HookCondition, HookHandler, HookContext, CallStackFrame, HookResult, HookRecord, } from './hook.js';
@@ -6,14 +6,22 @@ const currentFilename = fileURLToPath(import.meta.url);
6
6
  const currentDirname = dirname(currentFilename);
7
7
  export const projectRoot = join(currentDirname, '..', '..');
8
8
  const envPath = join(projectRoot, '.env');
9
- const result = dotenvConfig({ path: envPath, quiet: true });
10
- if (result.error) {
11
- console.error('[Config] Warning: Failed to load .env file from configured path');
12
- console.error(`[Config] Error: ${result.error.message}`);
13
- console.error('[Config] Will use environment variables or defaults');
14
- }
15
- else if (process.env.DEBUG === 'true') {
16
- console.info('[Config] .env file loaded (debug mode)');
9
+ let envLoaded = false;
10
+ function loadEnvIfNeeded() {
11
+ if (envLoaded) {
12
+ return;
13
+ }
14
+ envLoaded = true;
15
+ const result = dotenvConfig({ path: envPath, quiet: true });
16
+ const errorCode = result.error?.code;
17
+ if (result.error && errorCode !== 'ENOENT') {
18
+ console.error('[Config] Warning: Failed to load .env file from configured path');
19
+ console.error(`[Config] Error: ${result.error.message}`);
20
+ console.error('[Config] Will use environment variables or defaults');
21
+ }
22
+ else if (!result.error && process.env.DEBUG === 'true') {
23
+ console.info('[Config] .env file loaded (debug mode)');
24
+ }
17
25
  }
18
26
  const envInt = (fallback) => z.string().optional()
19
27
  .transform((v) => (v ? parseInt(v, 10) : fallback))
@@ -34,7 +42,7 @@ const ConfigSchema = z.object({
34
42
  CHROME_PATH: z.string().optional(),
35
43
  BROWSER_EXECUTABLE_PATH: z.string().optional(),
36
44
  MCP_SERVER_NAME: z.string().optional().default('jshookmcp'),
37
- MCP_SERVER_VERSION: z.string().optional().default('0.1.0'),
45
+ MCP_SERVER_VERSION: z.string().optional().default('0.1.8'),
38
46
  ENABLE_CACHE: envBool(false),
39
47
  CACHE_DIR: z.string().optional().default('.cache'),
40
48
  CACHE_TTL: envInt(3600).pipe(z.number().min(0)),
@@ -42,6 +50,7 @@ const ConfigSchema = z.object({
42
50
  MAX_CODE_SIZE_MB: envInt(10).pipe(z.number().min(1).max(500)),
43
51
  });
44
52
  export function getConfig() {
53
+ loadEnvIfNeeded();
45
54
  const parsed = ConfigSchema.safeParse(process.env);
46
55
  if (!parsed.success) {
47
56
  const issues = parsed.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`);
@@ -77,7 +86,7 @@ export function getConfig() {
77
86
  },
78
87
  mcp: {
79
88
  name: env.MCP_SERVER_NAME || 'jshookmcp',
80
- version: env.MCP_SERVER_VERSION || '0.1.0',
89
+ version: env.MCP_SERVER_VERSION || '0.1.8',
81
90
  },
82
91
  cache: {
83
92
  enabled: parsed.success ? env.ENABLE_CACHE : process.env.ENABLE_CACHE === 'true',
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@jshookmcp/jshook",
3
- "version": "0.1.6",
4
- "packageManager": "pnpm@10.28.2",
3
+ "version": "0.1.8",
5
4
  "description": "MCP server with 244 built-in tools (236 domain tools across 16 domains) for AI-assisted JavaScript analysis and security analysis — browser automation, CDP debugging, network monitoring, JS hooks, code analysis, and workflow orchestration",
6
5
  "mcpName": "io.github.vmoranv/jshookmcp",
7
6
  "main": "dist/index.js",
8
7
  "type": "module",
9
8
  "files": [
10
9
  "dist",
11
- "src/native/scripts"
10
+ "src/native/scripts",
11
+ "scripts/postinstall.cjs"
12
12
  ],
13
13
  "bin": {
14
14
  "jshook": "dist/index.js",
@@ -26,31 +26,6 @@
26
26
  "registry": "https://registry.npmjs.org",
27
27
  "access": "public"
28
28
  },
29
- "scripts": {
30
- "build": "node scripts/clean-dist.mjs && tsc -p tsconfig.json && tsc-alias -p tsconfig.json --resolve-full-paths && pnpm -C packages/extension-sdk build && node scripts/copy-native-scripts.mjs",
31
- "typecheck": "tsc --noEmit -p tsconfig.json && pnpm -C packages/extension-sdk typecheck",
32
- "dev": "tsx --conditions=development watch src/index.ts",
33
- "start": "node dist/index.js",
34
- "doctor": "tsx src/cli/doctor.ts",
35
- "docs:generate": "node scripts/generate-vitepress-reference.mjs && prettier \"docs/reference/**/*.md\" \"docs/en/reference/**/*.md\" --write",
36
- "docs:dev": "vitepress dev docs --host 127.0.0.1 --port 5173",
37
- "docs:build": "pnpm run docs:generate && vitepress build docs",
38
- "docs:preview": "vitepress preview docs --host 127.0.0.1 --port 4173",
39
- "lint:md": "pnpm dlx markdownlint-cli2",
40
- "format:docs": "pnpm run docs:generate && prettier docs .github README.md README.zh.md CONTRIBUTING.md --write",
41
- "check:docs-format": "prettier docs .github README.md README.zh.md CONTRIBUTING.md --check",
42
- "lint": "eslint src --ext .ts",
43
- "format": "prettier --write \"src/**/*.ts\"",
44
- "test": "vitest run",
45
- "test:coverage": "cross-env ENABLE_INJECTION_TOOLS=true vitest run --coverage",
46
- "prepack": "pnpm run build",
47
- "audit:tools": "node scripts/audit-tools.mjs",
48
- "check": "pnpm run lint && pnpm run typecheck && pnpm run test",
49
- "package": "pnpm pack",
50
- "prepublishOnly": "pnpm run check",
51
- "postinstall": "lefthook install",
52
- "install:full": "pnpm install && pnpm exec camoufox-js fetch"
53
- },
54
29
  "keywords": [
55
30
  "mcp",
56
31
  "model-context-protocol",
@@ -94,6 +69,7 @@
94
69
  "openai": "^6.25.0",
95
70
  "ora": "^9.3.0",
96
71
  "rebrowser-puppeteer-core": "^24.8.1",
72
+ "webcrack": "^2.15.1",
97
73
  "zod": "^4.3.6"
98
74
  },
99
75
  "optionalDependencies": {
@@ -118,22 +94,32 @@
118
94
  "vitepress": "1.6.4",
119
95
  "vitest": "^4.0.18"
120
96
  },
121
- "pnpm": {
122
- "overrides": {
123
- "hono": "4.12.5",
124
- "@hono/node-server": "1.19.11",
125
- "express-rate-limit": "8.3.0",
126
- "basic-ftp": "5.2.0",
127
- "minimatch": "10.2.4",
128
- "esbuild": "0.27.3"
129
- },
130
- "onlyBuiltDependencies": [
131
- "better-sqlite3",
132
- "esbuild",
133
- "koffi"
134
- ]
135
- },
136
97
  "engines": {
137
98
  "node": ">=20.0.0"
99
+ },
100
+ "scripts": {
101
+ "build": "node scripts/clean-dist.mjs && tsc -p tsconfig.json && tsc-alias -p tsconfig.json --resolve-full-paths && pnpm -C packages/extension-sdk build && node scripts/copy-native-scripts.mjs && node scripts/fix-bin-permissions.mjs",
102
+ "typecheck": "tsc --noEmit -p tsconfig.json && pnpm -C packages/extension-sdk typecheck",
103
+ "dev": "tsx --conditions=development watch src/index.ts",
104
+ "start": "node dist/index.js",
105
+ "doctor": "tsx src/cli/doctor.ts",
106
+ "docs:generate": "node scripts/generate-vitepress-reference.mjs && prettier \"docs/reference/**/*.md\" \"docs/en/reference/**/*.md\" --write",
107
+ "docs:dev": "vitepress dev docs --host 127.0.0.1 --port 5173",
108
+ "docs:build": "pnpm run docs:generate && vitepress build docs",
109
+ "docs:preview": "vitepress preview docs --host 127.0.0.1 --port 4173",
110
+ "lint:md": "pnpm dlx markdownlint-cli2",
111
+ "format:docs": "pnpm run docs:generate && prettier docs .github README.md README.zh.md CONTRIBUTING.md --write",
112
+ "check:docs-format": "prettier docs .github README.md README.zh.md CONTRIBUTING.md --check",
113
+ "lint": "eslint src --ext .ts",
114
+ "format": "prettier --write \"src/**/*.ts\"",
115
+ "test": "vitest run",
116
+ "test:coverage": "cross-env ENABLE_INJECTION_TOOLS=true vitest run --coverage",
117
+ "package:verify-bin": "node scripts/verify-packed-bin.mjs",
118
+ "package:verify-install": "node scripts/verify-packed-install.mjs",
119
+ "audit:tools": "node scripts/audit-tools.mjs",
120
+ "check": "pnpm run lint && pnpm run typecheck && pnpm run test",
121
+ "package": "pnpm pack",
122
+ "postinstall": "node scripts/postinstall.cjs",
123
+ "install:full": "pnpm install && pnpm exec camoufox-js fetch"
138
124
  }
139
- }
125
+ }
@@ -0,0 +1,37 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const { spawnSync } = require('node:child_process');
4
+
5
+ const repoGitDir = path.join(process.cwd(), '.git');
6
+ const localBin = path.join(
7
+ process.cwd(),
8
+ 'node_modules',
9
+ '.bin',
10
+ process.platform === 'win32' ? 'lefthook.cmd' : 'lefthook'
11
+ );
12
+
13
+ if (!fs.existsSync(repoGitDir)) {
14
+ process.exit(0);
15
+ }
16
+
17
+ if (!fs.existsSync(localBin)) {
18
+ console.warn('[postinstall] lefthook not found locally; skipping git hook installation.');
19
+ process.exit(0);
20
+ }
21
+
22
+ const result = spawnSync(localBin, ['install'], {
23
+ stdio: 'inherit',
24
+ shell: process.platform === 'win32',
25
+ });
26
+
27
+ if (result.error) {
28
+ console.warn(
29
+ `[postinstall] lefthook install failed to spawn: ${result.error.message}; skipping git hook installation.`
30
+ );
31
+ } else if (result.status !== 0) {
32
+ console.warn(
33
+ `[postinstall] lefthook install exited with status ${result.status}; skipping git hook installation.`
34
+ );
35
+ }
36
+
37
+ process.exit(0);
@@ -1,12 +1,12 @@
1
- #!/bin/bash
2
- # enum-windows.sh
3
- # Linux window enumeration script (placeholder)
4
-
5
- TARGET_PID=$1
6
-
7
- # Use xdotool or wmctrl for window enumeration
8
- # This is a placeholder for future implementation
9
-
10
- if command -v xdotool &> /dev/null; then
11
- xdotool search --pid "$TARGET_PID" --name "" get-window-name
12
- fi
1
+ #!/bin/bash
2
+ # enum-windows.sh
3
+ # Linux window enumeration script (placeholder)
4
+
5
+ TARGET_PID=$1
6
+
7
+ # Use xdotool or wmctrl for window enumeration
8
+ # This is a placeholder for future implementation
9
+
10
+ if command -v xdotool &> /dev/null; then
11
+ xdotool search --pid "$TARGET_PID" --name "" get-window-name
12
+ fi
@@ -1,22 +1,22 @@
1
- -- enum-windows.applescript
2
- -- macOS window enumeration script (placeholder)
3
-
4
- param TargetPid
5
-
6
- -- AppleScript implementation for window enumeration
7
- -- This is a placeholder for future implementation
8
-
9
- tell application "System Events"
10
- set windowList to {}
11
- repeat with proc in (every process whose unix id is TargetPid)
12
- repeat with win in windows of proc
13
- set end of windowList to {¬
14
- title: name of win, ¬
15
- position: position of win, ¬
16
- size: size of win ¬
17
- }
18
- end repeat
19
- end repeat
20
- end tell
21
-
22
- return windowList
1
+ -- enum-windows.applescript
2
+ -- macOS window enumeration script (placeholder)
3
+
4
+ param TargetPid
5
+
6
+ -- AppleScript implementation for window enumeration
7
+ -- This is a placeholder for future implementation
8
+
9
+ tell application "System Events"
10
+ set windowList to {}
11
+ repeat with proc in (every process whose unix id is TargetPid)
12
+ repeat with win in windows of proc
13
+ set end of windowList to {¬
14
+ title: name of win, ¬
15
+ position: position of win, ¬
16
+ size: size of win ¬
17
+ }
18
+ end repeat
19
+ end repeat
20
+ end tell
21
+
22
+ return windowList
@@ -1,51 +1,51 @@
1
- param(
2
- [string]$ClassPattern
3
- )
4
-
5
- Add-Type @"
6
- using System;
7
- using System.Runtime.InteropServices;
8
- public class Win32 {
9
- [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string title);
10
- [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int pid);
11
- [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
12
- [DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder className, int maxCount);
13
- }
14
- "@
15
-
16
- $windows = @()
17
- $hwnd = [IntPtr]::Zero
18
- while ($true) {
19
- $hwnd = [Win32]::FindWindowEx([IntPtr]::Zero, $hwnd, $null, $null)
20
- if ($hwnd -eq [IntPtr]::Zero) { break }
21
-
22
- $className = New-Object System.Text.StringBuilder 256
23
- [Win32]::GetClassName($hwnd, $className, 256) | Out-Null
24
- $classNameStr = $className.ToString()
25
-
26
- # Support wildcard pattern matching
27
- $isMatch = $false
28
- if ($ClassPattern -eq $classNameStr) {
29
- $isMatch = $true
30
- } elseif ($ClassPattern.Contains('*')) {
31
- # Convert wildcard pattern to regex
32
- $regexPattern = [regex]::Escape($ClassPattern).Replace('\*', '.*')
33
- if ($classNameStr -match $regexPattern) {
34
- $isMatch = $true
35
- }
36
- }
37
-
38
- if ($isMatch) {
39
- $windowPid = 0
40
- [Win32]::GetWindowThreadProcessId($hwnd, [ref]$windowPid) | Out-Null
41
- $title = New-Object System.Text.StringBuilder 256
42
- [Win32]::GetWindowText($hwnd, $title, 256) | Out-Null
43
- $windows += @{
44
- Handle = $hwnd.ToString()
45
- Title = $title.ToString()
46
- ClassName = $classNameStr
47
- ProcessId = $windowPid
48
- }
49
- }
50
- }
51
- $windows | ConvertTo-Json -Compress
1
+ param(
2
+ [string]$ClassPattern
3
+ )
4
+
5
+ Add-Type @"
6
+ using System;
7
+ using System.Runtime.InteropServices;
8
+ public class Win32 {
9
+ [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string title);
10
+ [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int pid);
11
+ [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
12
+ [DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder className, int maxCount);
13
+ }
14
+ "@
15
+
16
+ $windows = @()
17
+ $hwnd = [IntPtr]::Zero
18
+ while ($true) {
19
+ $hwnd = [Win32]::FindWindowEx([IntPtr]::Zero, $hwnd, $null, $null)
20
+ if ($hwnd -eq [IntPtr]::Zero) { break }
21
+
22
+ $className = New-Object System.Text.StringBuilder 256
23
+ [Win32]::GetClassName($hwnd, $className, 256) | Out-Null
24
+ $classNameStr = $className.ToString()
25
+
26
+ # Support wildcard pattern matching
27
+ $isMatch = $false
28
+ if ($ClassPattern -eq $classNameStr) {
29
+ $isMatch = $true
30
+ } elseif ($ClassPattern.Contains('*')) {
31
+ # Convert wildcard pattern to regex
32
+ $regexPattern = [regex]::Escape($ClassPattern).Replace('\*', '.*')
33
+ if ($classNameStr -match $regexPattern) {
34
+ $isMatch = $true
35
+ }
36
+ }
37
+
38
+ if ($isMatch) {
39
+ $windowPid = 0
40
+ [Win32]::GetWindowThreadProcessId($hwnd, [ref]$windowPid) | Out-Null
41
+ $title = New-Object System.Text.StringBuilder 256
42
+ [Win32]::GetWindowText($hwnd, $title, 256) | Out-Null
43
+ $windows += @{
44
+ Handle = $hwnd.ToString()
45
+ Title = $title.ToString()
46
+ ClassName = $classNameStr
47
+ ProcessId = $windowPid
48
+ }
49
+ }
50
+ }
51
+ $windows | ConvertTo-Json -Compress
@@ -1,44 +1,44 @@
1
- param(
2
- [int]$TargetPid
3
- )
4
-
5
- Add-Type @"
6
- using System;
7
- using System.Runtime.InteropServices;
8
- public class Win32 {
9
- [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string title);
10
- [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int pid);
11
- [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
12
- [DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder className, int maxCount);
13
- [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
14
- [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
15
- }
16
- "@
17
-
18
- $windows = @()
19
- $hwnd = [IntPtr]::Zero
20
- while ($true) {
21
- $hwnd = [Win32]::FindWindowEx([IntPtr]::Zero, $hwnd, $null, $null)
22
- if ($hwnd -eq [IntPtr]::Zero) { break }
23
- $windowPid = 0
24
- [Win32]::GetWindowThreadProcessId($hwnd, [ref]$windowPid) | Out-Null
25
- if ($windowPid -eq $TargetPid) {
26
- $title = New-Object System.Text.StringBuilder 256
27
- $className = New-Object System.Text.StringBuilder 256
28
- [Win32]::GetWindowText($hwnd, $title, 256) | Out-Null
29
- [Win32]::GetClassName($hwnd, $className, 256) | Out-Null
30
- $rect = New-Object Win32+RECT
31
- [Win32]::GetWindowRect($hwnd, [ref]$rect) | Out-Null
32
- $windows += @{
33
- Handle = $hwnd.ToString()
34
- Title = $title.ToString()
35
- ClassName = $className.ToString()
36
- ProcessId = $windowPid
37
- Left = $rect.Left
38
- Top = $rect.Top
39
- Right = $rect.Right
40
- Bottom = $rect.Bottom
41
- }
42
- }
43
- }
44
- $windows | ConvertTo-Json -Compress
1
+ param(
2
+ [int]$TargetPid
3
+ )
4
+
5
+ Add-Type @"
6
+ using System;
7
+ using System.Runtime.InteropServices;
8
+ public class Win32 {
9
+ [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string title);
10
+ [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int pid);
11
+ [DllImport("user32.dll")] public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
12
+ [DllImport("user32.dll")] public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder className, int maxCount);
13
+ [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
14
+ [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
15
+ }
16
+ "@
17
+
18
+ $windows = @()
19
+ $hwnd = [IntPtr]::Zero
20
+ while ($true) {
21
+ $hwnd = [Win32]::FindWindowEx([IntPtr]::Zero, $hwnd, $null, $null)
22
+ if ($hwnd -eq [IntPtr]::Zero) { break }
23
+ $windowPid = 0
24
+ [Win32]::GetWindowThreadProcessId($hwnd, [ref]$windowPid) | Out-Null
25
+ if ($windowPid -eq $TargetPid) {
26
+ $title = New-Object System.Text.StringBuilder 256
27
+ $className = New-Object System.Text.StringBuilder 256
28
+ [Win32]::GetWindowText($hwnd, $title, 256) | Out-Null
29
+ [Win32]::GetClassName($hwnd, $className, 256) | Out-Null
30
+ $rect = New-Object Win32+RECT
31
+ [Win32]::GetWindowRect($hwnd, [ref]$rect) | Out-Null
32
+ $windows += @{
33
+ Handle = $hwnd.ToString()
34
+ Title = $title.ToString()
35
+ ClassName = $className.ToString()
36
+ ProcessId = $windowPid
37
+ Left = $rect.Left
38
+ Top = $rect.Top
39
+ Right = $rect.Right
40
+ Bottom = $rect.Bottom
41
+ }
42
+ }
43
+ }
44
+ $windows | ConvertTo-Json -Compress