ai-browser 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -7
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +2 -1
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/api/mcp-sse.d.ts.map +1 -1
- package/dist/api/mcp-sse.js +26 -8
- package/dist/api/mcp-sse.js.map +1 -1
- package/dist/api/routes.js +1 -1
- package/dist/api/routes.js.map +1 -1
- package/dist/browser/BrowserManager.d.ts +2 -1
- package/dist/browser/BrowserManager.d.ts.map +1 -1
- package/dist/browser/BrowserManager.js +50 -7
- package/dist/browser/BrowserManager.js.map +1 -1
- package/dist/browser/PageEventTracker.d.ts +1 -1
- package/dist/browser/PageEventTracker.d.ts.map +1 -1
- package/dist/browser/SessionManager.d.ts +1 -1
- package/dist/browser/SessionManager.d.ts.map +1 -1
- package/dist/browser/actions.d.ts +1 -1
- package/dist/browser/actions.d.ts.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/cli/mcp-stdio.js +1 -1
- package/dist/cli/mcp-stdio.js.map +1 -1
- package/dist/mcp/browser-mcp-server.d.ts +6 -9
- package/dist/mcp/browser-mcp-server.d.ts.map +1 -1
- package/dist/mcp/browser-mcp-server.js +125 -218
- package/dist/mcp/browser-mcp-server.js.map +1 -1
- package/dist/mcp/task-tools.d.ts +6 -0
- package/dist/mcp/task-tools.d.ts.map +1 -0
- package/dist/mcp/task-tools.js +303 -0
- package/dist/mcp/task-tools.js.map +1 -0
- package/dist/semantic/ContentExtractor.d.ts +1 -1
- package/dist/semantic/ContentExtractor.d.ts.map +1 -1
- package/dist/semantic/ElementCollector.d.ts +1 -1
- package/dist/semantic/ElementCollector.d.ts.map +1 -1
- package/dist/semantic/IframeHandler.d.ts +1 -1
- package/dist/semantic/IframeHandler.d.ts.map +1 -1
- package/dist/semantic/PageAnalyzer.d.ts +1 -1
- package/dist/semantic/PageAnalyzer.d.ts.map +1 -1
- package/dist/semantic/RegionDetector.d.ts +1 -1
- package/dist/semantic/RegionDetector.d.ts.map +1 -1
- package/dist/semantic/StateTracker.d.ts +1 -1
- package/dist/semantic/StateTracker.d.ts.map +1 -1
- package/dist/task/artifact-store.d.ts +36 -0
- package/dist/task/artifact-store.d.ts.map +1 -0
- package/dist/task/artifact-store.js +115 -0
- package/dist/task/artifact-store.js.map +1 -0
- package/dist/task/cancel-token.d.ts +13 -0
- package/dist/task/cancel-token.d.ts.map +1 -0
- package/dist/task/cancel-token.js +42 -0
- package/dist/task/cancel-token.js.map +1 -0
- package/dist/task/error-codes.d.ts +19 -0
- package/dist/task/error-codes.d.ts.map +1 -0
- package/dist/task/error-codes.js +22 -0
- package/dist/task/error-codes.js.map +1 -0
- package/dist/task/index.d.ts +17 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task/index.js +10 -0
- package/dist/task/index.js.map +1 -0
- package/dist/task/run-manager.d.ts +77 -0
- package/dist/task/run-manager.d.ts.map +1 -0
- package/dist/task/run-manager.js +286 -0
- package/dist/task/run-manager.js.map +1 -0
- package/dist/task/run-store.d.ts +39 -0
- package/dist/task/run-store.d.ts.map +1 -0
- package/dist/task/run-store.js +88 -0
- package/dist/task/run-store.js.map +1 -0
- package/dist/task/templates/batch-extract.d.ts +33 -0
- package/dist/task/templates/batch-extract.d.ts.map +1 -0
- package/dist/task/templates/batch-extract.js +153 -0
- package/dist/task/templates/batch-extract.js.map +1 -0
- package/dist/task/templates/login-keep-session.d.ts +34 -0
- package/dist/task/templates/login-keep-session.d.ts.map +1 -0
- package/dist/task/templates/login-keep-session.js +190 -0
- package/dist/task/templates/login-keep-session.js.map +1 -0
- package/dist/task/templates/multi-tab-compare.d.ts +43 -0
- package/dist/task/templates/multi-tab-compare.d.ts.map +1 -0
- package/dist/task/templates/multi-tab-compare.js +204 -0
- package/dist/task/templates/multi-tab-compare.js.map +1 -0
- package/dist/task/templates/registry.d.ts +13 -0
- package/dist/task/templates/registry.d.ts.map +1 -0
- package/dist/task/templates/registry.js +40 -0
- package/dist/task/templates/registry.js.map +1 -0
- package/dist/task/tool-actions.d.ts +114 -0
- package/dist/task/tool-actions.d.ts.map +1 -0
- package/dist/task/tool-actions.js +371 -0
- package/dist/task/tool-actions.js.map +1 -0
- package/dist/task/tool-context.d.ts +26 -0
- package/dist/task/tool-context.d.ts.map +1 -0
- package/dist/task/tool-context.js +2 -0
- package/dist/task/tool-context.js.map +1 -0
- package/dist/utils/url-validator.d.ts +13 -0
- package/dist/utils/url-validator.d.ts.map +1 -1
- package/dist/utils/url-validator.js +64 -0
- package/dist/utils/url-validator.js.map +1 -1
- package/package.json +4 -3
- package/public/agent.html +3 -927
- package/public/index.html +1910 -664
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ToolContext } from './tool-context.js';
|
|
2
|
+
export interface NavigateResult {
|
|
3
|
+
success: boolean;
|
|
4
|
+
partial: boolean;
|
|
5
|
+
statusCode?: number;
|
|
6
|
+
page: {
|
|
7
|
+
url: string;
|
|
8
|
+
title: string;
|
|
9
|
+
};
|
|
10
|
+
dialog?: any;
|
|
11
|
+
}
|
|
12
|
+
export interface StabilityResult {
|
|
13
|
+
stable: boolean;
|
|
14
|
+
domStable: boolean;
|
|
15
|
+
networkPending: number;
|
|
16
|
+
loadState: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PageInfoResult {
|
|
19
|
+
page: {
|
|
20
|
+
url: string;
|
|
21
|
+
title: string;
|
|
22
|
+
type: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
};
|
|
25
|
+
elements: any[];
|
|
26
|
+
totalElements: number;
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
regions: any[];
|
|
29
|
+
intents: any[];
|
|
30
|
+
stability?: any;
|
|
31
|
+
pendingDialog?: any;
|
|
32
|
+
}
|
|
33
|
+
export interface PageContentResult {
|
|
34
|
+
sections: any[];
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}
|
|
37
|
+
export interface CreateTabResult {
|
|
38
|
+
tabId: string;
|
|
39
|
+
url: string;
|
|
40
|
+
partial: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface FindElementResult {
|
|
43
|
+
query: string;
|
|
44
|
+
candidates: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
type: string;
|
|
48
|
+
score: number;
|
|
49
|
+
matchReason: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export interface ClickResult {
|
|
53
|
+
success: boolean;
|
|
54
|
+
page: {
|
|
55
|
+
url: string;
|
|
56
|
+
title: string;
|
|
57
|
+
};
|
|
58
|
+
newTabCreated?: string;
|
|
59
|
+
dialog?: any;
|
|
60
|
+
}
|
|
61
|
+
export interface TypeTextResult {
|
|
62
|
+
success: boolean;
|
|
63
|
+
page: {
|
|
64
|
+
url: string;
|
|
65
|
+
title: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface PressKeyResult {
|
|
69
|
+
success: boolean;
|
|
70
|
+
page: {
|
|
71
|
+
url: string;
|
|
72
|
+
title: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export interface WaitResult {
|
|
76
|
+
success: boolean;
|
|
77
|
+
}
|
|
78
|
+
/** 导航到指定 URL(操作 active tab) */
|
|
79
|
+
export declare function navigate(ctx: ToolContext, sessionId: string, url: string): Promise<NavigateResult>;
|
|
80
|
+
/** 等待页面 DOM 稳定(支持显式 tabId) */
|
|
81
|
+
export declare function waitForStable(ctx: ToolContext, sessionId: string, tabId: string, opts?: {
|
|
82
|
+
timeout?: number;
|
|
83
|
+
quietMs?: number;
|
|
84
|
+
}): Promise<StabilityResult>;
|
|
85
|
+
/** 获取页面语义信息(支持显式 tabId) */
|
|
86
|
+
export declare function getPageInfo(ctx: ToolContext, sessionId: string, tabId: string, opts?: {
|
|
87
|
+
maxElements?: number;
|
|
88
|
+
visibleOnly?: boolean;
|
|
89
|
+
}): Promise<PageInfoResult>;
|
|
90
|
+
/** 提取页面文本内容(支持显式 tabId) */
|
|
91
|
+
export declare function getPageContent(ctx: ToolContext, sessionId: string, tabId: string, opts?: {
|
|
92
|
+
maxLength?: number;
|
|
93
|
+
}): Promise<PageContentResult>;
|
|
94
|
+
/** 创建新标签页(可选导航到 URL) */
|
|
95
|
+
export declare function createTab(ctx: ToolContext, sessionId: string, url?: string): Promise<CreateTabResult>;
|
|
96
|
+
/** 关闭指定标签页 */
|
|
97
|
+
export declare function closeTab(ctx: ToolContext, sessionId: string, tabId: string): Promise<{
|
|
98
|
+
success: boolean;
|
|
99
|
+
}>;
|
|
100
|
+
/** 模糊搜索页面元素 */
|
|
101
|
+
export declare function findElement(ctx: ToolContext, sessionId: string, tabId: string, query: string, limit?: number): Promise<FindElementResult>;
|
|
102
|
+
/** 点击元素(支持显式 tabId) */
|
|
103
|
+
export declare function click(ctx: ToolContext, sessionId: string, tabId: string, elementId: string): Promise<ClickResult>;
|
|
104
|
+
/** 输入文本(支持显式 tabId) */
|
|
105
|
+
export declare function typeText(ctx: ToolContext, sessionId: string, tabId: string, elementId: string, text: string, submit?: boolean): Promise<TypeTextResult>;
|
|
106
|
+
/** 按下键盘按键(支持显式 tabId + 修饰键组合) */
|
|
107
|
+
export declare function pressKey(ctx: ToolContext, sessionId: string, tabId: string, key: string, modifiers?: string[]): Promise<PressKeyResult>;
|
|
108
|
+
/** 条件等待(支持显式 tabId) */
|
|
109
|
+
export declare function wait(ctx: ToolContext, sessionId: string, tabId: string, opts: {
|
|
110
|
+
condition?: 'time' | 'selector' | 'networkidle' | 'element_hidden';
|
|
111
|
+
milliseconds?: number;
|
|
112
|
+
selector?: string;
|
|
113
|
+
}): Promise<WaitResult>;
|
|
114
|
+
//# sourceMappingURL=tool-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-actions.d.ts","sourceRoot":"","sources":["../../src/task/tool-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAsBrD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,MAAM,CAAC,EAAE,GAAG,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,aAAa,CAAC,EAAE,GAAG,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,GAAG,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB;AAYD,+BAA+B;AAC/B,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,cAAc,CAAC,CAuCzB;AAED,8BAA8B;AAC9B,wBAAsB,aAAa,CACjC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,eAAe,CAAC,CAa1B;AAED,2BAA2B;AAC3B,wBAAsB,WAAW,CAC/B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD,OAAO,CAAC,cAAc,CAAC,CAsEzB;AAED,2BAA2B;AAC3B,wBAAsB,cAAc,CAClC,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CA0B5B;AAED,wBAAwB;AACxB,wBAAsB,SAAS,CAC7B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,CA6B1B;AAED,cAAc;AACd,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAK/B;AAED,eAAe;AACf,wBAAsB,WAAW,CAC/B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAkB5B;AAED,uBAAuB;AACvB,wBAAsB,KAAK,CACzB,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAgCtB;AAED,uBAAuB;AACvB,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAyCD,iCAAiC;AACjC,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,cAAc,CAAC,CA+BzB;AAED,uBAAuB;AACvB,wBAAsB,IAAI,CACxB,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IACJ,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,aAAa,GAAG,gBAAgB,CAAC;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,UAAU,CAAC,CAoBrB"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { validateUrlAsync } from '../utils/url-validator.js';
|
|
2
|
+
import { ElementCollector, ElementMatcher, PageAnalyzer, RegionDetector, ContentExtractor, } from '../semantic/index.js';
|
|
3
|
+
import { executeAction } from '../browser/actions.js';
|
|
4
|
+
import { ErrorCode } from './error-codes.js';
|
|
5
|
+
// Shared semantic module instances
|
|
6
|
+
const elementCollector = new ElementCollector();
|
|
7
|
+
const elementMatcher = new ElementMatcher();
|
|
8
|
+
const pageAnalyzer = new PageAnalyzer();
|
|
9
|
+
const regionDetector = new RegionDetector();
|
|
10
|
+
const contentExtractor = new ContentExtractor();
|
|
11
|
+
// ===== Helper =====
|
|
12
|
+
function makeError(message, code) {
|
|
13
|
+
const err = new Error(message);
|
|
14
|
+
err.errorCode = code;
|
|
15
|
+
return err;
|
|
16
|
+
}
|
|
17
|
+
// ===== Tool Actions =====
|
|
18
|
+
/** 导航到指定 URL(操作 active tab) */
|
|
19
|
+
export async function navigate(ctx, sessionId, url) {
|
|
20
|
+
const check = await validateUrlAsync(url, ctx.urlOpts);
|
|
21
|
+
if (!check.valid) {
|
|
22
|
+
throw makeError(check.reason, ErrorCode.INVALID_PARAMETER);
|
|
23
|
+
}
|
|
24
|
+
const tab = ctx.getActiveTab(sessionId);
|
|
25
|
+
await ctx.sessionManager.syncHeadfulCookies();
|
|
26
|
+
await ctx.injectCookies(tab.page);
|
|
27
|
+
let partial = false;
|
|
28
|
+
let statusCode;
|
|
29
|
+
try {
|
|
30
|
+
const response = await tab.page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
31
|
+
statusCode = response?.status();
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err.name === 'TimeoutError' || err.message?.includes('timeout')) {
|
|
35
|
+
partial = true;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
throw new Error(err.message || 'Navigation failed');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// SPA 额外渲染时间
|
|
42
|
+
try {
|
|
43
|
+
await tab.page.waitForNetworkIdle({ timeout: 3000 });
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
tab.url = tab.page.url();
|
|
47
|
+
await ctx.saveCookies(tab.page);
|
|
48
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
49
|
+
let title = '';
|
|
50
|
+
try {
|
|
51
|
+
title = await tab.page.title();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
title = '(无法获取标题)';
|
|
55
|
+
}
|
|
56
|
+
const result = { success: true, partial, statusCode, page: { url: tab.page.url(), title } };
|
|
57
|
+
if (tab.events) {
|
|
58
|
+
const pending = tab.events.getPendingDialog();
|
|
59
|
+
if (pending)
|
|
60
|
+
result.dialog = pending;
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
/** 等待页面 DOM 稳定(支持显式 tabId) */
|
|
65
|
+
export async function waitForStable(ctx, sessionId, tabId, opts) {
|
|
66
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
67
|
+
if (!tab)
|
|
68
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
69
|
+
if (!tab.events) {
|
|
70
|
+
return { stable: true, domStable: true, networkPending: 0, loadState: 'loaded' };
|
|
71
|
+
}
|
|
72
|
+
const maxWait = Math.min(opts?.timeout ?? 5000, 30000);
|
|
73
|
+
const stable = await tab.events.waitForStable(maxWait, opts?.quietMs);
|
|
74
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
75
|
+
const state = tab.events.getStabilityState();
|
|
76
|
+
return { ...state, stable };
|
|
77
|
+
}
|
|
78
|
+
/** 获取页面语义信息(支持显式 tabId) */
|
|
79
|
+
export async function getPageInfo(ctx, sessionId, tabId, opts) {
|
|
80
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
81
|
+
if (!tab)
|
|
82
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
83
|
+
const limit = opts?.maxElements ?? 50;
|
|
84
|
+
const filterVisible = opts?.visibleOnly ?? true;
|
|
85
|
+
const [elements, analysis, regions] = await Promise.all([
|
|
86
|
+
elementCollector.collect(tab.page),
|
|
87
|
+
pageAnalyzer.analyze(tab.page),
|
|
88
|
+
regionDetector.detect(tab.page),
|
|
89
|
+
]);
|
|
90
|
+
let filtered = elements;
|
|
91
|
+
// Filter to viewport-visible elements
|
|
92
|
+
if (filterVisible) {
|
|
93
|
+
const viewport = tab.page.viewport();
|
|
94
|
+
if (viewport) {
|
|
95
|
+
filtered = filtered.filter((el) => {
|
|
96
|
+
const b = el.bounds;
|
|
97
|
+
if (!b || (b.width === 0 && b.height === 0))
|
|
98
|
+
return true;
|
|
99
|
+
return b.y + b.height > 0 && b.y < viewport.height
|
|
100
|
+
&& b.x + b.width > 0 && b.x < viewport.width;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Sort by y position and truncate
|
|
105
|
+
const totalElements = filtered.length;
|
|
106
|
+
filtered.sort((a, b) => (a.bounds?.y ?? 0) - (b.bounds?.y ?? 0));
|
|
107
|
+
const truncated = filtered.length > limit;
|
|
108
|
+
if (truncated)
|
|
109
|
+
filtered = filtered.slice(0, limit);
|
|
110
|
+
// Mask sensitive field values
|
|
111
|
+
for (const el of filtered) {
|
|
112
|
+
if (el.type === 'textbox' || el.type === 'input') {
|
|
113
|
+
const idLower = (el.id || '').toLowerCase();
|
|
114
|
+
const labelLower = (el.label || '').toLowerCase();
|
|
115
|
+
const isSensitive = idLower.includes('password') || idLower.includes('secret') || idLower.includes('token')
|
|
116
|
+
|| labelLower.includes('password') || labelLower.includes('secret') || labelLower.includes('token');
|
|
117
|
+
if (isSensitive && el.state?.value) {
|
|
118
|
+
el.state.value = '********';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
123
|
+
const result = {
|
|
124
|
+
page: {
|
|
125
|
+
url: tab.page.url(),
|
|
126
|
+
title: await tab.page.title(),
|
|
127
|
+
type: analysis.pageType,
|
|
128
|
+
summary: analysis.summary,
|
|
129
|
+
},
|
|
130
|
+
elements: filtered,
|
|
131
|
+
totalElements,
|
|
132
|
+
truncated,
|
|
133
|
+
regions,
|
|
134
|
+
intents: analysis.intents,
|
|
135
|
+
};
|
|
136
|
+
if (tab.events) {
|
|
137
|
+
result.stability = tab.events.getStabilityState();
|
|
138
|
+
const pending = tab.events.getPendingDialog();
|
|
139
|
+
if (pending)
|
|
140
|
+
result.pendingDialog = pending;
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/** 提取页面文本内容(支持显式 tabId) */
|
|
145
|
+
export async function getPageContent(ctx, sessionId, tabId, opts) {
|
|
146
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
147
|
+
if (!tab)
|
|
148
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
149
|
+
const content = await contentExtractor.extract(tab.page);
|
|
150
|
+
if (opts?.maxLength && content.sections) {
|
|
151
|
+
let totalLen = 0;
|
|
152
|
+
const truncatedSections = [];
|
|
153
|
+
for (const section of content.sections) {
|
|
154
|
+
const sectionText = section.text || '';
|
|
155
|
+
if (totalLen + sectionText.length > opts.maxLength) {
|
|
156
|
+
const remaining = opts.maxLength - totalLen;
|
|
157
|
+
if (remaining > 0) {
|
|
158
|
+
truncatedSections.push({ ...section, text: sectionText.slice(0, remaining) });
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
truncatedSections.push(section);
|
|
163
|
+
totalLen += sectionText.length;
|
|
164
|
+
}
|
|
165
|
+
content.sections = truncatedSections;
|
|
166
|
+
}
|
|
167
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
168
|
+
return content;
|
|
169
|
+
}
|
|
170
|
+
/** 创建新标签页(可选导航到 URL) */
|
|
171
|
+
export async function createTab(ctx, sessionId, url) {
|
|
172
|
+
const tab = await ctx.sessionManager.createTab(sessionId);
|
|
173
|
+
if (!tab)
|
|
174
|
+
throw makeError(`Session not found: ${sessionId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
175
|
+
// Auto-switch to the new tab
|
|
176
|
+
ctx.sessionManager.switchTab(sessionId, tab.id);
|
|
177
|
+
let partial = false;
|
|
178
|
+
if (url) {
|
|
179
|
+
const check = await validateUrlAsync(url, ctx.urlOpts);
|
|
180
|
+
if (!check.valid) {
|
|
181
|
+
throw makeError(check.reason, ErrorCode.INVALID_PARAMETER);
|
|
182
|
+
}
|
|
183
|
+
await ctx.injectCookies(tab.page);
|
|
184
|
+
try {
|
|
185
|
+
await tab.page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if (err.name === 'TimeoutError' || err.message?.includes('timeout')) {
|
|
189
|
+
partial = true;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
tab.url = tab.page.url();
|
|
196
|
+
await ctx.saveCookies(tab.page);
|
|
197
|
+
}
|
|
198
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
199
|
+
return { tabId: tab.id, url: tab.page.url(), partial };
|
|
200
|
+
}
|
|
201
|
+
/** 关闭指定标签页 */
|
|
202
|
+
export async function closeTab(ctx, sessionId, tabId) {
|
|
203
|
+
const closed = await ctx.sessionManager.closeTab(sessionId, tabId);
|
|
204
|
+
if (!closed)
|
|
205
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
206
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
207
|
+
return { success: true };
|
|
208
|
+
}
|
|
209
|
+
/** 模糊搜索页面元素 */
|
|
210
|
+
export async function findElement(ctx, sessionId, tabId, query, limit) {
|
|
211
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
212
|
+
if (!tab)
|
|
213
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
214
|
+
const elements = await elementCollector.collect(tab.page);
|
|
215
|
+
const candidates = elementMatcher.findByQuery(elements, query, limit ?? 5);
|
|
216
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
217
|
+
return {
|
|
218
|
+
query,
|
|
219
|
+
candidates: candidates.map((c) => ({
|
|
220
|
+
id: c.element.id,
|
|
221
|
+
label: c.element.label,
|
|
222
|
+
type: c.element.type,
|
|
223
|
+
score: c.score,
|
|
224
|
+
matchReason: c.matchReason,
|
|
225
|
+
})),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/** 点击元素(支持显式 tabId) */
|
|
229
|
+
export async function click(ctx, sessionId, tabId, elementId) {
|
|
230
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
231
|
+
if (!tab)
|
|
232
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
233
|
+
await executeAction(tab.page, 'click', elementId);
|
|
234
|
+
// Small delay to allow popup/dialog events to fire
|
|
235
|
+
await new Promise(r => setTimeout(r, 200));
|
|
236
|
+
tab.url = tab.page.url();
|
|
237
|
+
await ctx.saveCookies(tab.page);
|
|
238
|
+
// Check for popup windows captured by PageEventTracker
|
|
239
|
+
let newTabCreated;
|
|
240
|
+
if (tab.events) {
|
|
241
|
+
const popups = tab.events.getPopupPages();
|
|
242
|
+
for (const popupPage of popups) {
|
|
243
|
+
const newTab = await ctx.sessionManager.registerPopupAsTab(sessionId, popupPage);
|
|
244
|
+
if (newTab)
|
|
245
|
+
newTabCreated = newTab.id;
|
|
246
|
+
}
|
|
247
|
+
tab.events.clearPopupPages();
|
|
248
|
+
}
|
|
249
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
250
|
+
const result = {
|
|
251
|
+
success: true,
|
|
252
|
+
page: { url: tab.page.url(), title: await tab.page.title() },
|
|
253
|
+
};
|
|
254
|
+
if (newTabCreated)
|
|
255
|
+
result.newTabCreated = newTabCreated;
|
|
256
|
+
if (tab.events) {
|
|
257
|
+
const pending = tab.events.getPendingDialog();
|
|
258
|
+
if (pending)
|
|
259
|
+
result.dialog = pending;
|
|
260
|
+
}
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
/** 输入文本(支持显式 tabId) */
|
|
264
|
+
export async function typeText(ctx, sessionId, tabId, elementId, text, submit) {
|
|
265
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
266
|
+
if (!tab)
|
|
267
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
268
|
+
await executeAction(tab.page, 'type', elementId, text);
|
|
269
|
+
if (submit) {
|
|
270
|
+
await Promise.all([
|
|
271
|
+
tab.page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 5000 }).catch(() => { }),
|
|
272
|
+
tab.page.keyboard.press('Enter'),
|
|
273
|
+
]);
|
|
274
|
+
}
|
|
275
|
+
await ctx.saveCookies(tab.page);
|
|
276
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
277
|
+
return {
|
|
278
|
+
success: true,
|
|
279
|
+
page: { url: tab.page.url(), title: await tab.page.title() },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// ===== pressKey constants =====
|
|
283
|
+
const ALLOWED_KEYS = new Set([
|
|
284
|
+
'Enter', 'Escape', 'Tab', 'Backspace', 'Delete', 'Space',
|
|
285
|
+
'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',
|
|
286
|
+
'Home', 'End', 'PageUp', 'PageDown',
|
|
287
|
+
]);
|
|
288
|
+
const MODIFIER_COMBO_KEYS = new Set([
|
|
289
|
+
...ALLOWED_KEYS,
|
|
290
|
+
...'abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
291
|
+
...'0123456789'.split(''),
|
|
292
|
+
]);
|
|
293
|
+
const BLOCKED_COMBOS = [
|
|
294
|
+
{ modifiers: ['Control'], key: 'w' },
|
|
295
|
+
{ modifiers: ['Meta'], key: 'w' },
|
|
296
|
+
{ modifiers: ['Control', 'Shift'], key: 'I' },
|
|
297
|
+
{ modifiers: ['Meta', 'Shift'], key: 'I' },
|
|
298
|
+
{ modifiers: ['Control', 'Shift'], key: 'J' },
|
|
299
|
+
{ modifiers: ['Meta', 'Shift'], key: 'J' },
|
|
300
|
+
{ modifiers: ['Control', 'Shift'], key: 'Delete' },
|
|
301
|
+
{ modifiers: ['Control'], key: 'q' },
|
|
302
|
+
{ modifiers: ['Meta'], key: 'q' },
|
|
303
|
+
];
|
|
304
|
+
function isComboAllowed(modifiers, key) {
|
|
305
|
+
for (const blocked of BLOCKED_COMBOS) {
|
|
306
|
+
const modSet = new Set(modifiers);
|
|
307
|
+
const blockedSet = new Set(blocked.modifiers);
|
|
308
|
+
if (modSet.size === blockedSet.size &&
|
|
309
|
+
[...blockedSet].every(m => modSet.has(m)) &&
|
|
310
|
+
key.toLowerCase() === blocked.key.toLowerCase()) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return MODIFIER_COMBO_KEYS.has(key);
|
|
315
|
+
}
|
|
316
|
+
/** 按下键盘按键(支持显式 tabId + 修饰键组合) */
|
|
317
|
+
export async function pressKey(ctx, sessionId, tabId, key, modifiers) {
|
|
318
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
319
|
+
if (!tab)
|
|
320
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
321
|
+
if (modifiers && modifiers.length > 0) {
|
|
322
|
+
if (!isComboAllowed(modifiers, key)) {
|
|
323
|
+
throw makeError(`不允许的组合键: ${modifiers.join('+')}+${key}`, ErrorCode.INVALID_PARAMETER);
|
|
324
|
+
}
|
|
325
|
+
for (const mod of modifiers) {
|
|
326
|
+
await tab.page.keyboard.down(mod);
|
|
327
|
+
}
|
|
328
|
+
await tab.page.keyboard.press(key);
|
|
329
|
+
for (let i = modifiers.length - 1; i >= 0; i--) {
|
|
330
|
+
await tab.page.keyboard.up(modifiers[i]);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
if (!ALLOWED_KEYS.has(key)) {
|
|
335
|
+
throw makeError(`不允许的按键: ${key}。允许: ${[...ALLOWED_KEYS].join(', ')}`, ErrorCode.INVALID_PARAMETER);
|
|
336
|
+
}
|
|
337
|
+
await tab.page.keyboard.press(key);
|
|
338
|
+
}
|
|
339
|
+
await new Promise(r => setTimeout(r, 300));
|
|
340
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
341
|
+
return {
|
|
342
|
+
success: true,
|
|
343
|
+
page: { url: tab.page.url(), title: await tab.page.title() },
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/** 条件等待(支持显式 tabId) */
|
|
347
|
+
export async function wait(ctx, sessionId, tabId, opts) {
|
|
348
|
+
const tab = ctx.getTab(sessionId, tabId);
|
|
349
|
+
if (!tab)
|
|
350
|
+
throw makeError(`Tab not found: ${tabId}`, ErrorCode.SESSION_NOT_FOUND);
|
|
351
|
+
const { condition, milliseconds, selector } = opts;
|
|
352
|
+
if (condition === 'networkidle') {
|
|
353
|
+
await tab.page.waitForNetworkIdle({ timeout: milliseconds || 10000 });
|
|
354
|
+
}
|
|
355
|
+
else if (condition === 'element_hidden') {
|
|
356
|
+
if (!selector)
|
|
357
|
+
throw makeError('selector is required for element_hidden condition', ErrorCode.INVALID_PARAMETER);
|
|
358
|
+
await tab.page.waitForSelector(selector, { hidden: true, timeout: milliseconds || 10000 });
|
|
359
|
+
}
|
|
360
|
+
else if (condition === 'selector' || (!condition && selector)) {
|
|
361
|
+
if (!selector)
|
|
362
|
+
throw makeError('selector is required for selector condition', ErrorCode.INVALID_PARAMETER);
|
|
363
|
+
await tab.page.waitForSelector(selector, { timeout: milliseconds || 10000 });
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
await new Promise(r => setTimeout(r, Math.min(milliseconds || 1000, 30000)));
|
|
367
|
+
}
|
|
368
|
+
ctx.sessionManager.updateActivity(sessionId);
|
|
369
|
+
return { success: true };
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=tool-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-actions.js","sourceRoot":"","sources":["../../src/task/tool-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Page } from 'puppeteer-core';
|
|
2
|
+
import type { SessionManager, Tab } from '../browser/index.js';
|
|
3
|
+
import type { CookieStore } from '../browser/CookieStore.js';
|
|
4
|
+
import type { ValidateUrlOptions } from '../utils/url-validator.js';
|
|
5
|
+
export type TrustLevel = 'local' | 'remote';
|
|
6
|
+
/**
|
|
7
|
+
* 工具执行上下文 — 由 MCP handler 或模板执行器构造。
|
|
8
|
+
* 所有 toolAction 函数通过此接口获取依赖,不直接引用全局状态。
|
|
9
|
+
*/
|
|
10
|
+
export interface ToolContext {
|
|
11
|
+
sessionManager: SessionManager;
|
|
12
|
+
cookieStore?: CookieStore;
|
|
13
|
+
urlOpts: ValidateUrlOptions;
|
|
14
|
+
trustLevel: TrustLevel;
|
|
15
|
+
/** 解析 sessionId(MCP: defaultSession 逻辑;模板: 直接返回绑定 sessionId) */
|
|
16
|
+
resolveSession(sessionId?: string): Promise<string>;
|
|
17
|
+
/** 获取指定 session 的活跃 tab */
|
|
18
|
+
getActiveTab(sessionId: string): Tab;
|
|
19
|
+
/** 获取指定 session 的指定 tab(并发 tab 场景) */
|
|
20
|
+
getTab(sessionId: string, tabId: string): Tab | undefined;
|
|
21
|
+
/** 注入 cookie 到页面(CDP) */
|
|
22
|
+
injectCookies(page: Page): Promise<void>;
|
|
23
|
+
/** 从页面保存 cookie(CDP) */
|
|
24
|
+
saveCookies(page: Page): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=tool-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-context.d.ts","sourceRoot":"","sources":["../../src/task/tool-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5C;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IAEvB,gEAAgE;IAChE,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,2BAA2B;IAC3B,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC;IAErC,sCAAsC;IACtC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;IAE1D,yBAAyB;IACzB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC,wBAAwB;IACxB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-context.js","sourceRoot":"","sources":["../../src/task/tool-context.ts"],"names":[],"mappings":""}
|
|
@@ -14,4 +14,17 @@ export declare function validateUrl(url: string, options?: ValidateUrlOptions):
|
|
|
14
14
|
valid: false;
|
|
15
15
|
reason: string;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Async URL validation with DNS resolution check.
|
|
19
|
+
* When blockPrivate=true and hostname is not an IP literal,
|
|
20
|
+
* resolves DNS and checks if the resolved IP is private (anti-DNS-rebinding).
|
|
21
|
+
* DNS failure → allow original URL (fail-open, avoids false negatives in restricted DNS envs).
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateUrlAsync(url: string, options?: ValidateUrlOptions): Promise<{
|
|
24
|
+
valid: true;
|
|
25
|
+
parsed: URL;
|
|
26
|
+
} | {
|
|
27
|
+
valid: false;
|
|
28
|
+
reason: string;
|
|
29
|
+
}>;
|
|
17
30
|
//# sourceMappingURL=url-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-validator.d.ts","sourceRoot":"","sources":["../../src/utils/url-validator.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"url-validator.d.ts","sourceRoot":"","sources":["../../src/utils/url-validator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kGAAkG;IAClG,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAsBD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAiE1I;AAeD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAuC1E"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* URL validation utility — blocks dangerous protocols and private/loopback addresses.
|
|
3
3
|
*/
|
|
4
|
+
import dns from 'node:dns';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
const dnsLookup = promisify(dns.lookup);
|
|
4
7
|
// RFC 1918 + loopback + link-local ranges
|
|
5
8
|
const PRIVATE_IP_PATTERNS = [
|
|
6
9
|
/^127\./, // 127.0.0.0/8
|
|
@@ -77,4 +80,65 @@ export function validateUrl(url, options = {}) {
|
|
|
77
80
|
}
|
|
78
81
|
return { valid: true, parsed };
|
|
79
82
|
}
|
|
83
|
+
/** Check if hostname is an IP literal (v4 or v6 bracket notation) */
|
|
84
|
+
function isIpLiteral(hostname) {
|
|
85
|
+
if (hostname.startsWith('['))
|
|
86
|
+
return true; // IPv6
|
|
87
|
+
return /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname);
|
|
88
|
+
}
|
|
89
|
+
function isPrivateIp(ip) {
|
|
90
|
+
for (const pattern of PRIVATE_IP_PATTERNS) {
|
|
91
|
+
if (pattern.test(ip))
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Async URL validation with DNS resolution check.
|
|
98
|
+
* When blockPrivate=true and hostname is not an IP literal,
|
|
99
|
+
* resolves DNS and checks if the resolved IP is private (anti-DNS-rebinding).
|
|
100
|
+
* DNS failure → allow original URL (fail-open, avoids false negatives in restricted DNS envs).
|
|
101
|
+
*/
|
|
102
|
+
export async function validateUrlAsync(url, options = {}) {
|
|
103
|
+
// Run synchronous checks first
|
|
104
|
+
const syncResult = validateUrl(url, options);
|
|
105
|
+
if (!syncResult.valid)
|
|
106
|
+
return syncResult;
|
|
107
|
+
// DNS check only needed when blockPrivate is enabled and hostname is not an IP literal
|
|
108
|
+
if (!options.blockPrivate)
|
|
109
|
+
return syncResult;
|
|
110
|
+
const { parsed } = syncResult;
|
|
111
|
+
if (parsed.protocol === 'file:')
|
|
112
|
+
return syncResult;
|
|
113
|
+
const hostname = parsed.hostname;
|
|
114
|
+
if (isIpLiteral(hostname))
|
|
115
|
+
return syncResult; // already checked by sync validator
|
|
116
|
+
// Resolve DNS and check resolved IP
|
|
117
|
+
try {
|
|
118
|
+
const { address } = await dnsLookup(hostname);
|
|
119
|
+
if (isPrivateIp(address)) {
|
|
120
|
+
return { valid: false, reason: `DNS resolved to private address: ${hostname} → ${address}` };
|
|
121
|
+
}
|
|
122
|
+
if (PRIVATE_HOSTNAMES.has(address)) {
|
|
123
|
+
return { valid: false, reason: `DNS resolved to private address: ${hostname} → ${address}` };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Retry once before giving up — handles transient DNS failures.
|
|
128
|
+
try {
|
|
129
|
+
const { address } = await dnsLookup(hostname);
|
|
130
|
+
if (isPrivateIp(address)) {
|
|
131
|
+
return { valid: false, reason: `DNS resolved to private address: ${hostname} → ${address}` };
|
|
132
|
+
}
|
|
133
|
+
if (PRIVATE_HOSTNAMES.has(address)) {
|
|
134
|
+
return { valid: false, reason: `DNS resolved to private address: ${hostname} → ${address}` };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Fail-open when DNS is unavailable so public URLs are not blocked by local resolver issues.
|
|
139
|
+
return syncResult;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return syncResult;
|
|
143
|
+
}
|
|
80
144
|
//# sourceMappingURL=url-validator.js.map
|