ultimate-playwright-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +26 -0
- package/README.md +286 -0
- package/dist/browser/cdp.d.ts +37 -0
- package/dist/browser/cdp.d.ts.map +1 -0
- package/dist/browser/cdp.helpers.d.ts +13 -0
- package/dist/browser/cdp.helpers.d.ts.map +1 -0
- package/dist/browser/cdp.helpers.js +38 -0
- package/dist/browser/cdp.helpers.js.map +1 -0
- package/dist/browser/cdp.js +77 -0
- package/dist/browser/cdp.js.map +1 -0
- package/dist/browser/chrome-tab-groups.d.ts +93 -0
- package/dist/browser/chrome-tab-groups.d.ts.map +1 -0
- package/dist/browser/chrome-tab-groups.js +458 -0
- package/dist/browser/chrome-tab-groups.js.map +1 -0
- package/dist/browser/chrome.d.ts +11 -0
- package/dist/browser/chrome.d.ts.map +1 -0
- package/dist/browser/chrome.js +74 -0
- package/dist/browser/chrome.js.map +1 -0
- package/dist/browser/pw-role-snapshot.d.ts +41 -0
- package/dist/browser/pw-role-snapshot.d.ts.map +1 -0
- package/dist/browser/pw-role-snapshot.js +346 -0
- package/dist/browser/pw-role-snapshot.js.map +1 -0
- package/dist/browser/pw-session.d.ts +145 -0
- package/dist/browser/pw-session.d.ts.map +1 -0
- package/dist/browser/pw-session.js +468 -0
- package/dist/browser/pw-session.js.map +1 -0
- package/dist/browser/pw-tools-activity.d.ts +27 -0
- package/dist/browser/pw-tools-activity.d.ts.map +1 -0
- package/dist/browser/pw-tools-activity.js +52 -0
- package/dist/browser/pw-tools-activity.js.map +1 -0
- package/dist/browser/pw-tools-downloads.d.ts +40 -0
- package/dist/browser/pw-tools-downloads.d.ts.map +1 -0
- package/dist/browser/pw-tools-downloads.js +191 -0
- package/dist/browser/pw-tools-downloads.js.map +1 -0
- package/dist/browser/pw-tools-interactions.d.ts +122 -0
- package/dist/browser/pw-tools-interactions.d.ts.map +1 -0
- package/dist/browser/pw-tools-interactions.js +434 -0
- package/dist/browser/pw-tools-interactions.js.map +1 -0
- package/dist/browser/pw-tools-responses.d.ts +19 -0
- package/dist/browser/pw-tools-responses.d.ts.map +1 -0
- package/dist/browser/pw-tools-responses.js +98 -0
- package/dist/browser/pw-tools-responses.js.map +1 -0
- package/dist/browser/pw-tools-shared.d.ts +12 -0
- package/dist/browser/pw-tools-shared.d.ts.map +1 -0
- package/dist/browser/pw-tools-shared.js +55 -0
- package/dist/browser/pw-tools-shared.js.map +1 -0
- package/dist/browser/pw-tools-snapshot.d.ts +70 -0
- package/dist/browser/pw-tools-snapshot.d.ts.map +1 -0
- package/dist/browser/pw-tools-snapshot.js +151 -0
- package/dist/browser/pw-tools-snapshot.js.map +1 -0
- package/dist/browser/pw-tools-state.d.ts +52 -0
- package/dist/browser/pw-tools-state.d.ts.map +1 -0
- package/dist/browser/pw-tools-state.js +159 -0
- package/dist/browser/pw-tools-state.js.map +1 -0
- package/dist/browser/pw-tools-storage.d.ts +53 -0
- package/dist/browser/pw-tools-storage.d.ts.map +1 -0
- package/dist/browser/pw-tools-storage.js +81 -0
- package/dist/browser/pw-tools-storage.js.map +1 -0
- package/dist/browser/pw-tools-trace.d.ts +18 -0
- package/dist/browser/pw-tools-trace.d.ts.map +1 -0
- package/dist/browser/pw-tools-trace.js +31 -0
- package/dist/browser/pw-tools-trace.js.map +1 -0
- package/dist/browser/tab-groups.d.ts +94 -0
- package/dist/browser/tab-groups.d.ts.map +1 -0
- package/dist/browser/tab-groups.js +303 -0
- package/dist/browser/tab-groups.js.map +1 -0
- package/dist/browser/target-id.d.ts +20 -0
- package/dist/browser/target-id.d.ts.map +1 -0
- package/dist/browser/target-id.js +29 -0
- package/dist/browser/target-id.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/chrome-daemon.d.ts +7 -0
- package/dist/daemon/chrome-daemon.d.ts.map +1 -0
- package/dist/daemon/chrome-daemon.js +264 -0
- package/dist/daemon/chrome-daemon.js.map +1 -0
- package/dist/daemon/cli.d.ts +6 -0
- package/dist/daemon/cli.d.ts.map +1 -0
- package/dist/daemon/cli.js +110 -0
- package/dist/daemon/cli.js.map +1 -0
- package/dist/daemon/manager.d.ts +22 -0
- package/dist/daemon/manager.d.ts.map +1 -0
- package/dist/daemon/manager.js +89 -0
- package/dist/daemon/manager.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +41 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +122 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/actions.d.ts +6 -0
- package/dist/mcp/tools/actions.d.ts.map +1 -0
- package/dist/mcp/tools/actions.js +214 -0
- package/dist/mcp/tools/actions.js.map +1 -0
- package/dist/mcp/tools/navigate.d.ts +6 -0
- package/dist/mcp/tools/navigate.d.ts.map +1 -0
- package/dist/mcp/tools/navigate.js +31 -0
- package/dist/mcp/tools/navigate.js.map +1 -0
- package/dist/mcp/tools/snapshot.d.ts +6 -0
- package/dist/mcp/tools/snapshot.d.ts.map +1 -0
- package/dist/mcp/tools/snapshot.js +26 -0
- package/dist/mcp/tools/snapshot.js.map +1 -0
- package/dist/mcp/tools/tab-group.d.ts +9 -0
- package/dist/mcp/tools/tab-group.d.ts.map +1 -0
- package/dist/mcp/tools/tab-group.js +173 -0
- package/dist/mcp/tools/tab-group.js.map +1 -0
- package/dist/mcp/tools/tabs.d.ts +6 -0
- package/dist/mcp/tools/tabs.d.ts.map +1 -0
- package/dist/mcp/tools/tabs.js +209 -0
- package/dist/mcp/tools/tabs.js.map +1 -0
- package/dist/utils/command-format.d.ts +6 -0
- package/dist/utils/command-format.d.ts.map +1 -0
- package/dist/utils/command-format.js +8 -0
- package/dist/utils/command-format.js.map +1 -0
- package/dist/utils/errors.d.ts +6 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +22 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/extension-installer.d.ts +16 -0
- package/dist/utils/extension-installer.d.ts.map +1 -0
- package/dist/utils/extension-installer.js +134 -0
- package/dist/utils/extension-installer.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted from OpenClaw (https://github.com/openclaw/openclaw)
|
|
3
|
+
* Original: src/browser/pw-tools-core.trace.ts
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { ensureContextState, getPageForTargetId } from "./pw-session.js";
|
|
7
|
+
export async function traceStartViaPlaywright(opts) {
|
|
8
|
+
const page = await getPageForTargetId(opts);
|
|
9
|
+
const context = page.context();
|
|
10
|
+
const ctxState = ensureContextState(context);
|
|
11
|
+
if (ctxState.traceActive) {
|
|
12
|
+
throw new Error("Trace already running. Stop the current trace before starting a new one.");
|
|
13
|
+
}
|
|
14
|
+
await context.tracing.start({
|
|
15
|
+
screenshots: opts.screenshots ?? true,
|
|
16
|
+
snapshots: opts.snapshots ?? true,
|
|
17
|
+
sources: opts.sources ?? false,
|
|
18
|
+
});
|
|
19
|
+
ctxState.traceActive = true;
|
|
20
|
+
}
|
|
21
|
+
export async function traceStopViaPlaywright(opts) {
|
|
22
|
+
const page = await getPageForTargetId(opts);
|
|
23
|
+
const context = page.context();
|
|
24
|
+
const ctxState = ensureContextState(context);
|
|
25
|
+
if (!ctxState.traceActive) {
|
|
26
|
+
throw new Error("No active trace. Start a trace before stopping it.");
|
|
27
|
+
}
|
|
28
|
+
await context.tracing.stop({ path: opts.path });
|
|
29
|
+
ctxState.traceActive = false;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=pw-tools-trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pw-tools-trace.js","sourceRoot":"","sources":["../../src/browser/pw-tools-trace.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAM7C;IACC,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;QAC1B,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;QACrC,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;KAC/B,CAAC,CAAC;IACH,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,IAI5C;IACC,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,QAAQ,CAAC,WAAW,GAAG,KAAK,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tab group ownership registry.
|
|
3
|
+
*
|
|
4
|
+
* Provides per-session tab isolation via named groups.
|
|
5
|
+
* State is persisted to a shared JSON file so multiple MCP stdio processes
|
|
6
|
+
* (each serving a different user/session) can coordinate.
|
|
7
|
+
*/
|
|
8
|
+
export interface TabGroup {
|
|
9
|
+
groupId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
color?: string;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
/** Chrome's internal visual group ID, if the companion extension is loaded */
|
|
14
|
+
chromeGroupId?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface TabEntry {
|
|
17
|
+
groupId: string;
|
|
18
|
+
addedAt: number;
|
|
19
|
+
/** Chrome's internal tab ID (from extension), if available */
|
|
20
|
+
chromeTabId?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TabGroupRegistry {
|
|
23
|
+
groups: Record<string, TabGroup>;
|
|
24
|
+
/** keyed by CDP targetId */
|
|
25
|
+
tabs: Record<string, TabEntry>;
|
|
26
|
+
/** Cached companion extension ID (discovered on first probe) */
|
|
27
|
+
extensionId?: string;
|
|
28
|
+
}
|
|
29
|
+
declare const VALID_COLORS: readonly ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan"];
|
|
30
|
+
export type TabGroupColor = (typeof VALID_COLORS)[number];
|
|
31
|
+
export declare function isValidColor(c: string): c is TabGroupColor;
|
|
32
|
+
/**
|
|
33
|
+
* Create a new tab group. Returns the created group.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createTabGroup(opts: {
|
|
36
|
+
name: string;
|
|
37
|
+
color?: string;
|
|
38
|
+
}): TabGroup;
|
|
39
|
+
/**
|
|
40
|
+
* List all tab groups, optionally with tab counts.
|
|
41
|
+
*/
|
|
42
|
+
export declare function listTabGroups(): Array<TabGroup & {
|
|
43
|
+
tabCount: number;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Delete a tab group. Returns the targetIds of tabs that were in it
|
|
47
|
+
* (caller is responsible for actually closing them if desired).
|
|
48
|
+
*/
|
|
49
|
+
export declare function deleteTabGroup(groupId: string): {
|
|
50
|
+
removedTargetIds: string[];
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Associate a targetId with a group.
|
|
54
|
+
*/
|
|
55
|
+
export declare function addTabToGroup(targetId: string, groupId: string, chromeTabId?: number): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get the Chrome tab ID for a CDP targetId, if stored.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getChromeTabId(targetId: string): number | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Remove a targetId from whatever group it belongs to.
|
|
62
|
+
*/
|
|
63
|
+
export declare function removeTabFromGroup(targetId: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get the groupId for a given targetId, or null if unassigned.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getGroupForTab(targetId: string): string | null;
|
|
68
|
+
/**
|
|
69
|
+
* Get all targetIds belonging to a group.
|
|
70
|
+
*/
|
|
71
|
+
export declare function getTabsInGroup(groupId: string): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Prune targetIds that no longer exist in the browser.
|
|
74
|
+
* Pass in the set of live targetIds; any registry entries not in the set are removed.
|
|
75
|
+
*/
|
|
76
|
+
export declare function pruneStaleTargets(liveTargetIds: Set<string>): number;
|
|
77
|
+
/**
|
|
78
|
+
* Get a group by ID, or null.
|
|
79
|
+
*/
|
|
80
|
+
export declare function getTabGroup(groupId: string): TabGroup | null;
|
|
81
|
+
/**
|
|
82
|
+
* Get the cached companion extension ID.
|
|
83
|
+
*/
|
|
84
|
+
export declare function getExtensionId(): string | null;
|
|
85
|
+
/**
|
|
86
|
+
* Store the companion extension ID for future wake calls.
|
|
87
|
+
*/
|
|
88
|
+
export declare function setExtensionId(extensionId: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Store the Chrome visual group ID for a tab group.
|
|
91
|
+
*/
|
|
92
|
+
export declare function setChromeGroupId(groupId: string, chromeGroupId: number): void;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=tab-groups.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab-groups.d.ts","sourceRoot":"","sources":["../../src/browser/tab-groups.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjC,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/B,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AASD,QAAA,MAAM,YAAY,+EASR,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,aAAa,CAE1D;AA0GD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAY/E;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,KAAK,CAAC,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAWtE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAAE,CAe9E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ9D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAUxD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAWpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAQ5D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAQ9C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAIxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAO7E"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tab group ownership registry.
|
|
3
|
+
*
|
|
4
|
+
* Provides per-session tab isolation via named groups.
|
|
5
|
+
* State is persisted to a shared JSON file so multiple MCP stdio processes
|
|
6
|
+
* (each serving a different user/session) can coordinate.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { randomBytes } from "node:crypto";
|
|
12
|
+
// ── Constants ──────────────────────────────────────────────────────────────
|
|
13
|
+
const DATA_DIR = join(homedir(), ".ultimate-playwright-mcp");
|
|
14
|
+
const REGISTRY_PATH = join(DATA_DIR, "tab-groups.json");
|
|
15
|
+
const LOCK_PATH = REGISTRY_PATH + ".lock";
|
|
16
|
+
// Lock is considered stale after 5s (handled in acquireLock via timeout)
|
|
17
|
+
const VALID_COLORS = [
|
|
18
|
+
"grey",
|
|
19
|
+
"blue",
|
|
20
|
+
"red",
|
|
21
|
+
"yellow",
|
|
22
|
+
"green",
|
|
23
|
+
"pink",
|
|
24
|
+
"purple",
|
|
25
|
+
"cyan",
|
|
26
|
+
];
|
|
27
|
+
export function isValidColor(c) {
|
|
28
|
+
return VALID_COLORS.includes(c);
|
|
29
|
+
}
|
|
30
|
+
// ── File-level locking (simple, cross-process) ─────────────────────────────
|
|
31
|
+
function acquireLock(maxWaitMs = 3_000) {
|
|
32
|
+
const start = Date.now();
|
|
33
|
+
while (true) {
|
|
34
|
+
try {
|
|
35
|
+
// O_EXCL-style: writeFileSync with flag "wx" fails if file exists
|
|
36
|
+
writeFileSync(LOCK_PATH, String(process.pid), { flag: "wx" });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Check for stale lock
|
|
41
|
+
try {
|
|
42
|
+
const stat = readFileSync(LOCK_PATH, "utf-8");
|
|
43
|
+
const pid = parseInt(stat, 10);
|
|
44
|
+
if (pid && !isProcessAlive(pid)) {
|
|
45
|
+
unlinkSync(LOCK_PATH);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// lock file disappeared between our check and read — retry
|
|
51
|
+
}
|
|
52
|
+
if (Date.now() - start > maxWaitMs) {
|
|
53
|
+
// Force-break stale lock
|
|
54
|
+
try {
|
|
55
|
+
unlinkSync(LOCK_PATH);
|
|
56
|
+
}
|
|
57
|
+
catch { /* ignore */ }
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Spin-wait briefly
|
|
61
|
+
const wait = 20 + Math.random() * 30;
|
|
62
|
+
const until = Date.now() + wait;
|
|
63
|
+
while (Date.now() < until) {
|
|
64
|
+
/* busy wait — we're in sync code */
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function releaseLock() {
|
|
70
|
+
try {
|
|
71
|
+
unlinkSync(LOCK_PATH);
|
|
72
|
+
}
|
|
73
|
+
catch { /* ignore */ }
|
|
74
|
+
}
|
|
75
|
+
function isProcessAlive(pid) {
|
|
76
|
+
try {
|
|
77
|
+
process.kill(pid, 0);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// ── Registry I/O ───────────────────────────────────────────────────────────
|
|
85
|
+
function ensureDir() {
|
|
86
|
+
if (!existsSync(DATA_DIR)) {
|
|
87
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function readRegistry() {
|
|
91
|
+
ensureDir();
|
|
92
|
+
if (!existsSync(REGISTRY_PATH)) {
|
|
93
|
+
return { groups: {}, tabs: {} };
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const raw = readFileSync(REGISTRY_PATH, "utf-8");
|
|
97
|
+
const parsed = JSON.parse(raw);
|
|
98
|
+
return {
|
|
99
|
+
groups: parsed.groups ?? {},
|
|
100
|
+
tabs: parsed.tabs ?? {},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return { groups: {}, tabs: {} };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function writeRegistry(reg) {
|
|
108
|
+
ensureDir();
|
|
109
|
+
writeFileSync(REGISTRY_PATH, JSON.stringify(reg, null, 2), "utf-8");
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Run a read-modify-write operation on the registry with file locking.
|
|
113
|
+
*/
|
|
114
|
+
function withRegistry(fn) {
|
|
115
|
+
acquireLock();
|
|
116
|
+
try {
|
|
117
|
+
const reg = readRegistry();
|
|
118
|
+
const result = fn(reg);
|
|
119
|
+
writeRegistry(reg);
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
releaseLock();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
127
|
+
function generateGroupId() {
|
|
128
|
+
return "g_" + randomBytes(8).toString("hex");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create a new tab group. Returns the created group.
|
|
132
|
+
*/
|
|
133
|
+
export function createTabGroup(opts) {
|
|
134
|
+
const color = opts.color && isValidColor(opts.color) ? opts.color : undefined;
|
|
135
|
+
const group = {
|
|
136
|
+
groupId: generateGroupId(),
|
|
137
|
+
name: opts.name.trim() || "unnamed",
|
|
138
|
+
color,
|
|
139
|
+
createdAt: Date.now(),
|
|
140
|
+
};
|
|
141
|
+
withRegistry((reg) => {
|
|
142
|
+
reg.groups[group.groupId] = group;
|
|
143
|
+
});
|
|
144
|
+
return group;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* List all tab groups, optionally with tab counts.
|
|
148
|
+
*/
|
|
149
|
+
export function listTabGroups() {
|
|
150
|
+
acquireLock();
|
|
151
|
+
try {
|
|
152
|
+
const reg = readRegistry();
|
|
153
|
+
return Object.values(reg.groups).map((g) => ({
|
|
154
|
+
...g,
|
|
155
|
+
tabCount: Object.values(reg.tabs).filter((t) => t.groupId === g.groupId).length,
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
releaseLock();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Delete a tab group. Returns the targetIds of tabs that were in it
|
|
164
|
+
* (caller is responsible for actually closing them if desired).
|
|
165
|
+
*/
|
|
166
|
+
export function deleteTabGroup(groupId) {
|
|
167
|
+
const removed = [];
|
|
168
|
+
withRegistry((reg) => {
|
|
169
|
+
if (!reg.groups[groupId]) {
|
|
170
|
+
throw new Error(`Tab group not found: ${groupId}`);
|
|
171
|
+
}
|
|
172
|
+
delete reg.groups[groupId];
|
|
173
|
+
for (const [targetId, entry] of Object.entries(reg.tabs)) {
|
|
174
|
+
if (entry.groupId === groupId) {
|
|
175
|
+
removed.push(targetId);
|
|
176
|
+
delete reg.tabs[targetId];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return { removedTargetIds: removed };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Associate a targetId with a group.
|
|
184
|
+
*/
|
|
185
|
+
export function addTabToGroup(targetId, groupId, chromeTabId) {
|
|
186
|
+
withRegistry((reg) => {
|
|
187
|
+
if (!reg.groups[groupId]) {
|
|
188
|
+
throw new Error(`Tab group not found: ${groupId}`);
|
|
189
|
+
}
|
|
190
|
+
reg.tabs[targetId] = { groupId, addedAt: Date.now(), chromeTabId };
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get the Chrome tab ID for a CDP targetId, if stored.
|
|
195
|
+
*/
|
|
196
|
+
export function getChromeTabId(targetId) {
|
|
197
|
+
acquireLock();
|
|
198
|
+
try {
|
|
199
|
+
const reg = readRegistry();
|
|
200
|
+
return reg.tabs[targetId]?.chromeTabId;
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
releaseLock();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Remove a targetId from whatever group it belongs to.
|
|
208
|
+
*/
|
|
209
|
+
export function removeTabFromGroup(targetId) {
|
|
210
|
+
withRegistry((reg) => {
|
|
211
|
+
delete reg.tabs[targetId];
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get the groupId for a given targetId, or null if unassigned.
|
|
216
|
+
*/
|
|
217
|
+
export function getGroupForTab(targetId) {
|
|
218
|
+
acquireLock();
|
|
219
|
+
try {
|
|
220
|
+
const reg = readRegistry();
|
|
221
|
+
return reg.tabs[targetId]?.groupId ?? null;
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
releaseLock();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get all targetIds belonging to a group.
|
|
229
|
+
*/
|
|
230
|
+
export function getTabsInGroup(groupId) {
|
|
231
|
+
acquireLock();
|
|
232
|
+
try {
|
|
233
|
+
const reg = readRegistry();
|
|
234
|
+
return Object.entries(reg.tabs)
|
|
235
|
+
.filter(([, entry]) => entry.groupId === groupId)
|
|
236
|
+
.map(([targetId]) => targetId);
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
releaseLock();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Prune targetIds that no longer exist in the browser.
|
|
244
|
+
* Pass in the set of live targetIds; any registry entries not in the set are removed.
|
|
245
|
+
*/
|
|
246
|
+
export function pruneStaleTargets(liveTargetIds) {
|
|
247
|
+
let pruned = 0;
|
|
248
|
+
withRegistry((reg) => {
|
|
249
|
+
for (const targetId of Object.keys(reg.tabs)) {
|
|
250
|
+
if (!liveTargetIds.has(targetId)) {
|
|
251
|
+
delete reg.tabs[targetId];
|
|
252
|
+
pruned++;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return pruned;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get a group by ID, or null.
|
|
260
|
+
*/
|
|
261
|
+
export function getTabGroup(groupId) {
|
|
262
|
+
acquireLock();
|
|
263
|
+
try {
|
|
264
|
+
const reg = readRegistry();
|
|
265
|
+
return reg.groups[groupId] ?? null;
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
releaseLock();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the cached companion extension ID.
|
|
273
|
+
*/
|
|
274
|
+
export function getExtensionId() {
|
|
275
|
+
acquireLock();
|
|
276
|
+
try {
|
|
277
|
+
const reg = readRegistry();
|
|
278
|
+
return reg.extensionId ?? null;
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
releaseLock();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Store the companion extension ID for future wake calls.
|
|
286
|
+
*/
|
|
287
|
+
export function setExtensionId(extensionId) {
|
|
288
|
+
withRegistry((reg) => {
|
|
289
|
+
reg.extensionId = extensionId;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Store the Chrome visual group ID for a tab group.
|
|
294
|
+
*/
|
|
295
|
+
export function setChromeGroupId(groupId, chromeGroupId) {
|
|
296
|
+
withRegistry((reg) => {
|
|
297
|
+
const group = reg.groups[groupId];
|
|
298
|
+
if (group) {
|
|
299
|
+
group.chromeGroupId = chromeGroupId;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=tab-groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab-groups.js","sourceRoot":"","sources":["../../src/browser/tab-groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted from OpenClaw (https://github.com/openclaw/openclaw)
|
|
3
|
+
* Original: src/browser/target-id.ts
|
|
4
|
+
* License: MIT
|
|
5
|
+
*
|
|
6
|
+
* Modified for ultimate-playwright-mcp:
|
|
7
|
+
* - No changes needed - utility is standalone
|
|
8
|
+
*/
|
|
9
|
+
export type TargetIdResolution = {
|
|
10
|
+
ok: true;
|
|
11
|
+
targetId: string;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
reason: "not_found" | "ambiguous";
|
|
15
|
+
matches?: string[];
|
|
16
|
+
};
|
|
17
|
+
export declare function resolveTargetIdFromTabs(input: string, tabs: Array<{
|
|
18
|
+
targetId: string;
|
|
19
|
+
}>): TargetIdResolution;
|
|
20
|
+
//# sourceMappingURL=target-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target-id.d.ts","sourceRoot":"","sources":["../../src/browser/target-id.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEzE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAChC,kBAAkB,CAsBpB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted from OpenClaw (https://github.com/openclaw/openclaw)
|
|
3
|
+
* Original: src/browser/target-id.ts
|
|
4
|
+
* License: MIT
|
|
5
|
+
*
|
|
6
|
+
* Modified for ultimate-playwright-mcp:
|
|
7
|
+
* - No changes needed - utility is standalone
|
|
8
|
+
*/
|
|
9
|
+
export function resolveTargetIdFromTabs(input, tabs) {
|
|
10
|
+
const needle = input.trim();
|
|
11
|
+
if (!needle) {
|
|
12
|
+
return { ok: false, reason: "not_found" };
|
|
13
|
+
}
|
|
14
|
+
const exact = tabs.find((t) => t.targetId === needle);
|
|
15
|
+
if (exact) {
|
|
16
|
+
return { ok: true, targetId: exact.targetId };
|
|
17
|
+
}
|
|
18
|
+
const lower = needle.toLowerCase();
|
|
19
|
+
const matches = tabs.map((t) => t.targetId).filter((id) => id.toLowerCase().startsWith(lower));
|
|
20
|
+
const only = matches.length === 1 ? matches[0] : undefined;
|
|
21
|
+
if (only) {
|
|
22
|
+
return { ok: true, targetId: only };
|
|
23
|
+
}
|
|
24
|
+
if (matches.length === 0) {
|
|
25
|
+
return { ok: false, reason: "not_found" };
|
|
26
|
+
}
|
|
27
|
+
return { ok: false, reason: "ambiguous", matches };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=target-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target-id.js","sourceRoot":"","sources":["../../src/browser/target-id.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,UAAU,uBAAuB,CACrC,KAAa,EACb,IAAiC;IAEjC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC;IACtD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAE/F,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC5C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACrD,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;GAEG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for ultimate-playwright-mcp
|
|
4
|
+
*/
|
|
5
|
+
import { program } from "commander";
|
|
6
|
+
import { runServer } from "./mcp/server.js";
|
|
7
|
+
import { validateConfig } from "./config.js";
|
|
8
|
+
program
|
|
9
|
+
.name("ultimate-playwright-mcp")
|
|
10
|
+
.description("Multi-agent Playwright MCP server with tab isolation via targetId")
|
|
11
|
+
.version("0.1.0")
|
|
12
|
+
.option("--cdp-endpoint <url>", "CDP endpoint URL (default: auto-start daemon on localhost:9223)", process.env.CDP_ENDPOINT)
|
|
13
|
+
.option("--agent-id <id>", "Optional agent ID for logging/debugging", process.env.AGENT_ID)
|
|
14
|
+
.option("--chrome-user-data-dir <path>", "Chrome profile directory (default: ~/.ultimate-playwright-mcp/chrome-profile)", process.env.CHROME_USER_DATA_DIR)
|
|
15
|
+
.option("--chrome-extensions <ids>", "Chrome Web Store extension IDs (comma-separated)", process.env.CHROME_EXTENSIONS)
|
|
16
|
+
.option("--chrome-executable <path>", "Path to Chrome executable (auto-detected if not provided)", process.env.CHROME_EXECUTABLE)
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
const config = {
|
|
19
|
+
cdpEndpoint: options.cdpEndpoint,
|
|
20
|
+
agentId: options.agentId,
|
|
21
|
+
chromeUserDataDir: options.chromeUserDataDir,
|
|
22
|
+
chromeExtensions: options.chromeExtensions?.split(",").map((s) => s.trim()).filter(Boolean),
|
|
23
|
+
chromeExecutable: options.chromeExecutable,
|
|
24
|
+
};
|
|
25
|
+
try {
|
|
26
|
+
validateConfig(config);
|
|
27
|
+
await runServer(config);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
program.parse();
|
|
35
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO;KACJ,IAAI,CAAC,yBAAyB,CAAC;KAC/B,WAAW,CAAC,mEAAmE,CAAC;KAChF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CACL,sBAAsB,EACtB,iEAAiE,EACjE,OAAO,CAAC,GAAG,CAAC,YAAY,CACzB;KACA,MAAM,CAAC,iBAAiB,EAAE,yCAAyC,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;KAC1F,MAAM,CACL,+BAA+B,EAC/B,+EAA+E,EAC/E,OAAO,CAAC,GAAG,CAAC,oBAAoB,CACjC;KACA,MAAM,CACL,2BAA2B,EAC3B,kDAAkD,EAClD,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAC9B;KACA,MAAM,CACL,4BAA4B,EAC5B,2DAA2D,EAC3D,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAC9B;KACA,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,MAAM,GAAiB;QAC3B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACnG,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC;IAEF,IAAI,CAAC;QACH,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for ultimate-playwright-mcp server
|
|
3
|
+
*/
|
|
4
|
+
export interface ServerConfig {
|
|
5
|
+
/**
|
|
6
|
+
* CDP endpoint URL (e.g., "http://localhost:9222")
|
|
7
|
+
* If not provided, the Chrome daemon will be auto-started on localhost:9223
|
|
8
|
+
*/
|
|
9
|
+
cdpEndpoint?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional agent ID for logging/debugging
|
|
12
|
+
*/
|
|
13
|
+
agentId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Chrome user data directory (profile directory)
|
|
16
|
+
* Default: ~/.ultimate-playwright-mcp/chrome-profile
|
|
17
|
+
*/
|
|
18
|
+
chromeUserDataDir?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Chrome extensions to load:
|
|
21
|
+
* - Chrome Web Store IDs (32 lowercase letters, e.g., "nngceckbapebfimnlniiiahkandclblb" for Bitwarden)
|
|
22
|
+
* - Local paths to unpacked extensions (absolute or relative paths)
|
|
23
|
+
* Extensions from the store will be automatically downloaded and extracted.
|
|
24
|
+
*/
|
|
25
|
+
chromeExtensions?: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Chrome executable path (auto-detected if not provided)
|
|
28
|
+
*/
|
|
29
|
+
chromeExecutable?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function validateConfig(_config: ServerConfig): void;
|
|
32
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAE1D"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkCH,MAAM,UAAU,cAAc,CAAC,OAAqB;IAClD,gEAAgE;AAClE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrome-daemon.d.ts","sourceRoot":"","sources":["../../src/daemon/chrome-daemon.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
|