@jshookmcp/jshook 0.1.6 → 0.1.7
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/LICENSE +661 -661
- package/dist/index.js +0 -0
- package/dist/modules/captcha/AICaptchaDetector.js +185 -185
- package/dist/modules/process/MacProcessManager.js +25 -25
- package/dist/modules/process/memory/availability.js +49 -49
- package/dist/modules/process/memory/injector.js +185 -185
- package/dist/modules/process/memory/reader.js +50 -50
- package/dist/modules/process/memory/scanner.js +165 -165
- package/dist/modules/process/memory/writer.js +54 -54
- package/dist/native/scripts/linux/enum-windows.sh +12 -12
- package/dist/native/scripts/macos/enum-windows.applescript +22 -22
- package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
- package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
- package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
- package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
- package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
- package/dist/server/domains/browser/definitions.tools.security.js +76 -76
- package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
- package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
- package/package.json +26 -43
- package/src/native/scripts/linux/enum-windows.sh +12 -12
- package/src/native/scripts/macos/enum-windows.applescript +22 -22
- package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
- package/src/native/scripts/windows/enum-windows.ps1 +44 -44
- package/src/native/scripts/windows/inject-dll.ps1 +21 -21
|
@@ -28,98 +28,98 @@ export const CRYPTO_KEYWORDS = [
|
|
|
28
28
|
'aes',
|
|
29
29
|
'rsa',
|
|
30
30
|
];
|
|
31
|
-
const CRYPTO_TEST_WORKER_SCRIPT = `
|
|
32
|
-
const __bootstrap = async () => {
|
|
33
|
-
const [workerThreads, vm, perfHooks] = await Promise.all([
|
|
34
|
-
import('node:worker_threads'),
|
|
35
|
-
import('node:vm'),
|
|
36
|
-
import('node:perf_hooks'),
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
const parentPort = workerThreads.parentPort;
|
|
40
|
-
const performance = perfHooks.performance;
|
|
41
|
-
|
|
42
|
-
if (!parentPort) {
|
|
43
|
-
throw new Error('worker parentPort is unavailable');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function normalizeOutput(value) {
|
|
47
|
-
if (value === undefined) return '__undefined__';
|
|
48
|
-
if (value === null) return 'null';
|
|
49
|
-
if (typeof value === 'string') return value;
|
|
50
|
-
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
|
|
51
|
-
try { return JSON.stringify(value); } catch { return String(value); }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
parentPort.on('message', async (msg) => {
|
|
55
|
-
const { jobId, payload } = msg;
|
|
56
|
-
try {
|
|
57
|
-
const { code, functionName, testInputs } = payload;
|
|
58
|
-
const sandbox = {
|
|
59
|
-
console: { log() {}, warn() {}, error() {} },
|
|
60
|
-
Buffer,
|
|
61
|
-
TextEncoder,
|
|
62
|
-
TextDecoder,
|
|
63
|
-
atob: (v) => Buffer.from(String(v), 'base64').toString('binary'),
|
|
64
|
-
btoa: (v) => Buffer.from(String(v), 'binary').toString('base64'),
|
|
65
|
-
};
|
|
66
|
-
sandbox.globalThis = sandbox;
|
|
67
|
-
const context = vm.createContext(sandbox);
|
|
68
|
-
|
|
69
|
-
const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(functionName);
|
|
70
|
-
const bindCode = isValidIdentifier
|
|
71
|
-
? "\\n;globalThis.__targetFn = (typeof " + functionName + " !== 'undefined' ? " + functionName + " : globalThis[" + JSON.stringify(functionName) + "]);"
|
|
72
|
-
: "\\n;globalThis.__targetFn = globalThis[" + JSON.stringify(functionName) + "];";
|
|
73
|
-
|
|
74
|
-
const script = new vm.Script(code + bindCode, { timeout: 5000 });
|
|
75
|
-
script.runInContext(context, { timeout: 5000 });
|
|
76
|
-
|
|
77
|
-
const targetFn = context.__targetFn;
|
|
78
|
-
if (typeof targetFn !== 'function') {
|
|
79
|
-
throw new Error("Function not found or not callable: " + functionName);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const rows = [];
|
|
83
|
-
for (const input of testInputs) {
|
|
84
|
-
const started = performance.now();
|
|
85
|
-
try {
|
|
86
|
-
const raw = targetFn(input);
|
|
87
|
-
const resolved = raw && typeof raw.then === 'function' ? await raw : raw;
|
|
88
|
-
rows.push({
|
|
89
|
-
input,
|
|
90
|
-
output: normalizeOutput(resolved),
|
|
91
|
-
duration: Number((performance.now() - started).toFixed(3)),
|
|
92
|
-
});
|
|
93
|
-
} catch (err) {
|
|
94
|
-
rows.push({
|
|
95
|
-
input,
|
|
96
|
-
output: '',
|
|
97
|
-
error: err && err.message ? err.message : String(err),
|
|
98
|
-
duration: Number((performance.now() - started).toFixed(3)),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
parentPort.postMessage({ jobId, ok: true, result: { ok: true, results: rows } });
|
|
104
|
-
} catch (error) {
|
|
105
|
-
parentPort.postMessage({
|
|
106
|
-
jobId,
|
|
107
|
-
ok: true,
|
|
108
|
-
result: {
|
|
109
|
-
ok: false,
|
|
110
|
-
error: error && error.message ? error.message : String(error),
|
|
111
|
-
results: [],
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
__bootstrap().catch((error) => {
|
|
119
|
-
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
120
|
-
console.error('crypto harness worker bootstrap failed:', error && error.message ? error.message : String(error));
|
|
121
|
-
}
|
|
122
|
-
});
|
|
31
|
+
const CRYPTO_TEST_WORKER_SCRIPT = `
|
|
32
|
+
const __bootstrap = async () => {
|
|
33
|
+
const [workerThreads, vm, perfHooks] = await Promise.all([
|
|
34
|
+
import('node:worker_threads'),
|
|
35
|
+
import('node:vm'),
|
|
36
|
+
import('node:perf_hooks'),
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const parentPort = workerThreads.parentPort;
|
|
40
|
+
const performance = perfHooks.performance;
|
|
41
|
+
|
|
42
|
+
if (!parentPort) {
|
|
43
|
+
throw new Error('worker parentPort is unavailable');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeOutput(value) {
|
|
47
|
+
if (value === undefined) return '__undefined__';
|
|
48
|
+
if (value === null) return 'null';
|
|
49
|
+
if (typeof value === 'string') return value;
|
|
50
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
|
|
51
|
+
try { return JSON.stringify(value); } catch { return String(value); }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
parentPort.on('message', async (msg) => {
|
|
55
|
+
const { jobId, payload } = msg;
|
|
56
|
+
try {
|
|
57
|
+
const { code, functionName, testInputs } = payload;
|
|
58
|
+
const sandbox = {
|
|
59
|
+
console: { log() {}, warn() {}, error() {} },
|
|
60
|
+
Buffer,
|
|
61
|
+
TextEncoder,
|
|
62
|
+
TextDecoder,
|
|
63
|
+
atob: (v) => Buffer.from(String(v), 'base64').toString('binary'),
|
|
64
|
+
btoa: (v) => Buffer.from(String(v), 'binary').toString('base64'),
|
|
65
|
+
};
|
|
66
|
+
sandbox.globalThis = sandbox;
|
|
67
|
+
const context = vm.createContext(sandbox);
|
|
68
|
+
|
|
69
|
+
const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(functionName);
|
|
70
|
+
const bindCode = isValidIdentifier
|
|
71
|
+
? "\\n;globalThis.__targetFn = (typeof " + functionName + " !== 'undefined' ? " + functionName + " : globalThis[" + JSON.stringify(functionName) + "]);"
|
|
72
|
+
: "\\n;globalThis.__targetFn = globalThis[" + JSON.stringify(functionName) + "];";
|
|
73
|
+
|
|
74
|
+
const script = new vm.Script(code + bindCode, { timeout: 5000 });
|
|
75
|
+
script.runInContext(context, { timeout: 5000 });
|
|
76
|
+
|
|
77
|
+
const targetFn = context.__targetFn;
|
|
78
|
+
if (typeof targetFn !== 'function') {
|
|
79
|
+
throw new Error("Function not found or not callable: " + functionName);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const rows = [];
|
|
83
|
+
for (const input of testInputs) {
|
|
84
|
+
const started = performance.now();
|
|
85
|
+
try {
|
|
86
|
+
const raw = targetFn(input);
|
|
87
|
+
const resolved = raw && typeof raw.then === 'function' ? await raw : raw;
|
|
88
|
+
rows.push({
|
|
89
|
+
input,
|
|
90
|
+
output: normalizeOutput(resolved),
|
|
91
|
+
duration: Number((performance.now() - started).toFixed(3)),
|
|
92
|
+
});
|
|
93
|
+
} catch (err) {
|
|
94
|
+
rows.push({
|
|
95
|
+
input,
|
|
96
|
+
output: '',
|
|
97
|
+
error: err && err.message ? err.message : String(err),
|
|
98
|
+
duration: Number((performance.now() - started).toFixed(3)),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
parentPort.postMessage({ jobId, ok: true, result: { ok: true, results: rows } });
|
|
104
|
+
} catch (error) {
|
|
105
|
+
parentPort.postMessage({
|
|
106
|
+
jobId,
|
|
107
|
+
ok: true,
|
|
108
|
+
result: {
|
|
109
|
+
ok: false,
|
|
110
|
+
error: error && error.message ? error.message : String(error),
|
|
111
|
+
results: [],
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
__bootstrap().catch((error) => {
|
|
119
|
+
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
120
|
+
console.error('crypto harness worker bootstrap failed:', error && error.message ? error.message : String(error));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
123
|
`;
|
|
124
124
|
export class TransformToolHandlersBase {
|
|
125
125
|
collector;
|
|
@@ -333,16 +333,16 @@ export class TransformToolHandlersBase {
|
|
|
333
333
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
|
|
334
334
|
}
|
|
335
335
|
buildCryptoPolyfills() {
|
|
336
|
-
return `
|
|
337
|
-
const __textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
|
|
338
|
-
const __textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
|
|
339
|
-
|
|
340
|
-
if (typeof globalThis.atob === 'undefined') {
|
|
341
|
-
globalThis.atob = (value) => Buffer.from(String(value), 'base64').toString('binary');
|
|
342
|
-
}
|
|
343
|
-
if (typeof globalThis.btoa === 'undefined') {
|
|
344
|
-
globalThis.btoa = (value) => Buffer.from(String(value), 'binary').toString('base64');
|
|
345
|
-
}
|
|
336
|
+
return `
|
|
337
|
+
const __textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
|
|
338
|
+
const __textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
|
|
339
|
+
|
|
340
|
+
if (typeof globalThis.atob === 'undefined') {
|
|
341
|
+
globalThis.atob = (value) => Buffer.from(String(value), 'base64').toString('binary');
|
|
342
|
+
}
|
|
343
|
+
if (typeof globalThis.btoa === 'undefined') {
|
|
344
|
+
globalThis.btoa = (value) => Buffer.from(String(value), 'binary').toString('base64');
|
|
345
|
+
}
|
|
346
346
|
`.trim();
|
|
347
347
|
}
|
|
348
348
|
async runCryptoHarness(code, functionName, testInputs) {
|
|
@@ -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
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jshookmcp/jshook",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"packageManager": "pnpm@10.28.2",
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|
|
@@ -26,31 +25,6 @@
|
|
|
26
25
|
"registry": "https://registry.npmjs.org",
|
|
27
26
|
"access": "public"
|
|
28
27
|
},
|
|
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
28
|
"keywords": [
|
|
55
29
|
"mcp",
|
|
56
30
|
"model-context-protocol",
|
|
@@ -118,22 +92,31 @@
|
|
|
118
92
|
"vitepress": "1.6.4",
|
|
119
93
|
"vitest": "^4.0.18"
|
|
120
94
|
},
|
|
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
95
|
"engines": {
|
|
137
96
|
"node": ">=20.0.0"
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"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",
|
|
100
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && pnpm -C packages/extension-sdk typecheck",
|
|
101
|
+
"dev": "tsx --conditions=development watch src/index.ts",
|
|
102
|
+
"start": "node dist/index.js",
|
|
103
|
+
"doctor": "tsx src/cli/doctor.ts",
|
|
104
|
+
"docs:generate": "node scripts/generate-vitepress-reference.mjs && prettier \"docs/reference/**/*.md\" \"docs/en/reference/**/*.md\" --write",
|
|
105
|
+
"docs:dev": "vitepress dev docs --host 127.0.0.1 --port 5173",
|
|
106
|
+
"docs:build": "pnpm run docs:generate && vitepress build docs",
|
|
107
|
+
"docs:preview": "vitepress preview docs --host 127.0.0.1 --port 4173",
|
|
108
|
+
"lint:md": "pnpm dlx markdownlint-cli2",
|
|
109
|
+
"format:docs": "pnpm run docs:generate && prettier docs .github README.md README.zh.md CONTRIBUTING.md --write",
|
|
110
|
+
"check:docs-format": "prettier docs .github README.md README.zh.md CONTRIBUTING.md --check",
|
|
111
|
+
"lint": "eslint src --ext .ts",
|
|
112
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
113
|
+
"test": "vitest run",
|
|
114
|
+
"test:coverage": "cross-env ENABLE_INJECTION_TOOLS=true vitest run --coverage",
|
|
115
|
+
"package:verify-bin": "node scripts/verify-packed-bin.mjs",
|
|
116
|
+
"audit:tools": "node scripts/audit-tools.mjs",
|
|
117
|
+
"check": "pnpm run lint && pnpm run typecheck && pnpm run test",
|
|
118
|
+
"package": "pnpm pack",
|
|
119
|
+
"postinstall": "lefthook install",
|
|
120
|
+
"install:full": "pnpm install && pnpm exec camoufox-js fetch"
|
|
138
121
|
}
|
|
139
|
-
}
|
|
122
|
+
}
|
|
@@ -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
|