vskill 1.2.3 → 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +49 -1
  2. package/agents.json +6 -1
  3. package/dist/agents/agents-registry.d.ts +43 -16
  4. package/dist/agents/agents-registry.js +119 -33
  5. package/dist/agents/agents-registry.js.map +1 -1
  6. package/dist/agents/global-dirs.d.ts +19 -0
  7. package/dist/agents/global-dirs.js +53 -0
  8. package/dist/agents/global-dirs.js.map +1 -0
  9. package/dist/commands/add-github-bundle.d.ts +29 -0
  10. package/dist/commands/add-github-bundle.js +135 -0
  11. package/dist/commands/add-github-bundle.js.map +1 -0
  12. package/dist/commands/add-lockfile.d.ts +2 -0
  13. package/dist/commands/add-lockfile.js +1 -1
  14. package/dist/commands/add-lockfile.js.map +1 -1
  15. package/dist/commands/add.d.ts +0 -7
  16. package/dist/commands/add.js +197 -107
  17. package/dist/commands/add.js.map +1 -1
  18. package/dist/commands/audit-webmcp.d.ts +25 -0
  19. package/dist/commands/audit-webmcp.js +167 -0
  20. package/dist/commands/audit-webmcp.js.map +1 -0
  21. package/dist/commands/doctor.d.ts +69 -0
  22. package/dist/commands/doctor.js +760 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/remove.js +43 -8
  25. package/dist/commands/remove.js.map +1 -1
  26. package/dist/commands/update.js +12 -4
  27. package/dist/commands/update.js.map +1 -1
  28. package/dist/discovery/github-skill-bundle.d.ts +74 -0
  29. package/dist/discovery/github-skill-bundle.js +323 -0
  30. package/dist/discovery/github-skill-bundle.js.map +1 -0
  31. package/dist/eval/skill-scanner.js +7 -1
  32. package/dist/eval/skill-scanner.js.map +1 -1
  33. package/dist/eval-server/install-skill-routes.js +10 -3
  34. package/dist/eval-server/install-skill-routes.js.map +1 -1
  35. package/dist/eval-server/remove-skill-routes.js +262 -29
  36. package/dist/eval-server/remove-skill-routes.js.map +1 -1
  37. package/dist/eval-server/studio-privacy.js +2 -3
  38. package/dist/eval-server/studio-privacy.js.map +1 -1
  39. package/dist/eval-server/utils/scan-install-locations.js +22 -3
  40. package/dist/eval-server/utils/scan-install-locations.js.map +1 -1
  41. package/dist/eval-ui/assets/{AdvancedTab-CSWhj_MZ.js → AdvancedTab-6VlfqCuD.js} +1 -1
  42. package/dist/eval-ui/assets/{CreateSkillPage-lecFvrLO.js → CreateSkillPage-CXDd6gDN.js} +1 -1
  43. package/dist/eval-ui/assets/{FindSkillsPalette-Dtyd7VrA.js → FindSkillsPalette-DQ6uGhFP.js} +2 -2
  44. package/dist/eval-ui/assets/{GeneralTab-Gbc-K6UI.js → GeneralTab-BpFlJ2ue.js} +1 -1
  45. package/dist/eval-ui/assets/{PrivacyTab-BXCIX8cQ.js → PrivacyTab-UfO7QLy_.js} +1 -1
  46. package/dist/eval-ui/assets/{SearchPaletteCore-CneELTjb.js → SearchPaletteCore-DpHCf0mf.js} +1 -1
  47. package/dist/eval-ui/assets/{SkillDetailPanel-BMfMN6Wy.js → SkillDetailPanel-BjHFjGxg.js} +1 -1
  48. package/dist/eval-ui/assets/{UpdateDropdown-BlAAI6ah.js → UpdateDropdown-Cn4Vsq5d.js} +1 -1
  49. package/dist/eval-ui/assets/{UpdatesTab-BFGUaUtu.js → UpdatesTab-C38-n7V3.js} +1 -1
  50. package/dist/eval-ui/assets/globals-BWCNQsMa.css +1 -0
  51. package/dist/eval-ui/assets/globals-DmzftdEG.js +49 -0
  52. package/dist/eval-ui/assets/{index-DhhmQddr.js → index-DlQYQHDO.js} +1 -1
  53. package/dist/eval-ui/assets/{lifecycle-o_IRibOa.js → lifecycle-n00yue6r.js} +1 -1
  54. package/dist/eval-ui/assets/main-BmB7Zj9I.js +86 -0
  55. package/dist/eval-ui/assets/{preferences-D2BPT0OT.js → preferences-BN-jNWS0.js} +2 -2
  56. package/dist/eval-ui/assets/{useDesktopBridge-BKz-gcRg.js → useDesktopBridge-Cm4zPght.js} +1 -1
  57. package/dist/eval-ui/index.html +4 -4
  58. package/dist/eval-ui/lifecycle.html +3 -3
  59. package/dist/eval-ui/preferences.html +4 -4
  60. package/dist/index.js +34 -0
  61. package/dist/index.js.map +1 -1
  62. package/dist/installer/bundle-files.d.ts +39 -2
  63. package/dist/installer/bundle-files.js +80 -10
  64. package/dist/installer/bundle-files.js.map +1 -1
  65. package/dist/installer/canonical.d.ts +39 -4
  66. package/dist/installer/canonical.js +178 -24
  67. package/dist/installer/canonical.js.map +1 -1
  68. package/dist/installer/multi-install.d.ts +11 -2
  69. package/dist/installer/multi-install.js +41 -9
  70. package/dist/installer/multi-install.js.map +1 -1
  71. package/dist/installer/transformers/index.d.ts +8 -0
  72. package/dist/installer/yaml-safe-mutate.js +48 -2
  73. package/dist/installer/yaml-safe-mutate.js.map +1 -1
  74. package/dist/lib/github-fetch.js +2 -0
  75. package/dist/lib/github-fetch.js.map +1 -1
  76. package/dist/lib/private-source.d.ts +8 -0
  77. package/dist/lib/private-source.js +16 -2
  78. package/dist/lib/private-source.js.map +1 -1
  79. package/dist/webmcp/args.d.ts +34 -0
  80. package/dist/webmcp/args.js +87 -0
  81. package/dist/webmcp/args.js.map +1 -0
  82. package/dist/webmcp/attribution.d.ts +38 -0
  83. package/dist/webmcp/attribution.js +141 -0
  84. package/dist/webmcp/attribution.js.map +1 -0
  85. package/dist/webmcp/enumerate.d.ts +81 -0
  86. package/dist/webmcp/enumerate.js +436 -0
  87. package/dist/webmcp/enumerate.js.map +1 -0
  88. package/dist/webmcp/format.d.ts +9 -0
  89. package/dist/webmcp/format.js +105 -0
  90. package/dist/webmcp/format.js.map +1 -0
  91. package/dist/webmcp/merge.d.ts +49 -0
  92. package/dist/webmcp/merge.js +167 -0
  93. package/dist/webmcp/merge.js.map +1 -0
  94. package/dist/webmcp/score.d.ts +70 -0
  95. package/dist/webmcp/score.js +586 -0
  96. package/dist/webmcp/score.js.map +1 -0
  97. package/dist/webmcp/types.d.ts +92 -0
  98. package/dist/webmcp/types.js +8 -0
  99. package/dist/webmcp/types.js.map +1 -0
  100. package/package.json +6 -6
  101. package/dist/eval-ui/assets/globals-Dpf9KmYH.css +0 -1
  102. package/dist/eval-ui/assets/globals-hm1COkXX.js +0 -49
  103. package/dist/eval-ui/assets/main-DRhfwCeX.js +0 -86
@@ -0,0 +1,141 @@
1
+ // ---------------------------------------------------------------------------
2
+ // vskill audit-webmcp -- who registered a tool. Pure module: no browser.
3
+ //
4
+ // first-party the script is on the page's own site (registrable domain)
5
+ // platform the page runs on a hosting platform and the script is that
6
+ // platform's own code (PLATFORM_HOSTS: host + path prefix,
7
+ // and only when the page carries the platform's marker)
8
+ // library the script comes from a public npm CDN (unpkg, jsDelivr,
9
+ // esm.sh, cdnjs, jspm, Skypack): a library the page author
10
+ // chose to load
11
+ // third-party any other site. It may still be the site's own asset domain
12
+ // (assets.targetimg1.com on target.com); we cannot tell.
13
+ // ---------------------------------------------------------------------------
14
+ import { siteOf } from "./score.js";
15
+ /**
16
+ * Hosting platforms whose own scripts register WebMCP tools on merchant
17
+ * pages. Add an entry only after seeing it in a scan, and keep the path
18
+ * prefix narrow: cdn.shopify.com/extensions/ holds third-party app code and
19
+ * cdn.shopify.com/s/files/ holds any merchant's uploads.
20
+ */
21
+ export const PLATFORM_HOSTS = [
22
+ // Shopify storefront WebMCP (seen 2026-10: 92 stores serve it from their own
23
+ // domain under /cdn/shopifycloud/, cotopaxi.com from cdn.shopify.com).
24
+ { platform: "Shopify", marker: "shopify", host: "cdn.shopify.com", pathPrefix: "/shopifycloud/" },
25
+ ];
26
+ /** Public npm CDNs: the page author picks the package and version. */
27
+ export const LIBRARY_CDN_HOSTS = [
28
+ "unpkg.com",
29
+ "cdn.jsdelivr.net",
30
+ "fastly.jsdelivr.net",
31
+ "gcore.jsdelivr.net",
32
+ "esm.sh",
33
+ "esm.run",
34
+ "cdnjs.cloudflare.com",
35
+ "ga.jspm.io",
36
+ "jspm.dev",
37
+ "cdn.skypack.dev",
38
+ ];
39
+ /** name or name@version from path segments starting at a package name (handles @scope/name). */
40
+ function packageAt(segs, i) {
41
+ const first = segs[i];
42
+ if (!first)
43
+ return undefined;
44
+ if (first.startsWith("@")) {
45
+ const second = segs[i + 1];
46
+ return second ? `${first}/${second}` : undefined;
47
+ }
48
+ return first;
49
+ }
50
+ /** The package a public-CDN URL loads, as the URL spells it (e.g. "simple-webmcp@0.3.0"). */
51
+ export function libraryPackage(url) {
52
+ let u;
53
+ try {
54
+ u = new URL(url);
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ const host = u.hostname.toLowerCase();
60
+ const segs = u.pathname.split("/").filter(Boolean).map((s) => {
61
+ try {
62
+ return decodeURIComponent(s);
63
+ }
64
+ catch {
65
+ return s;
66
+ }
67
+ });
68
+ if (host.endsWith("jsdelivr.net")) {
69
+ if (segs[0] === "npm")
70
+ return packageAt(segs, 1);
71
+ if (segs[0] === "gh" && segs[1] && segs[2])
72
+ return `gh/${segs[1]}/${segs[2]}`;
73
+ return undefined;
74
+ }
75
+ if (host === "cdnjs.cloudflare.com") {
76
+ if (segs[0] === "ajax" && segs[1] === "libs" && segs[2])
77
+ return segs[3] ? `${segs[2]}@${segs[3]}` : segs[2];
78
+ return undefined;
79
+ }
80
+ if (host === "ga.jspm.io" || host === "jspm.dev") {
81
+ const s = segs.slice();
82
+ if (s[0]?.startsWith("npm:"))
83
+ s[0] = s[0].slice(4);
84
+ return packageAt(s, 0);
85
+ }
86
+ if (host === "esm.sh") {
87
+ let i = 0;
88
+ while (segs[i] && (/^v\d+$/.test(segs[i]) || segs[i] === "stable"))
89
+ i++;
90
+ if (segs[i] === "gh" && segs[i + 1] && segs[i + 2])
91
+ return `gh/${segs[i + 1]}/${segs[i + 2]}`;
92
+ return packageAt(segs, i);
93
+ }
94
+ if (host === "cdn.skypack.dev") {
95
+ // Pinned URLs look like /-/name@v1.2.3-<hash>/dist=es2019/...
96
+ if (segs[0] === "-") {
97
+ const p = packageAt(segs, 1);
98
+ return p?.replace(/@v(\d[^-]*)-[A-Za-z0-9]+$/, "@$1");
99
+ }
100
+ return packageAt(segs, 0);
101
+ }
102
+ return packageAt(segs, 0);
103
+ }
104
+ /**
105
+ * True when a package spec has no exact version (no @version, or a tag such
106
+ * as @latest). A full commit SHA on a gh/ spec counts as pinned.
107
+ */
108
+ export function isUnpinned(pkg) {
109
+ const at = pkg.lastIndexOf("@");
110
+ if (at <= 0)
111
+ return true;
112
+ const version = pkg.slice(at + 1);
113
+ if (pkg.startsWith("gh/") && /^[0-9a-f]{40}$/i.test(version))
114
+ return false;
115
+ return !/^\d+\.\d+\.\d+/.test(version);
116
+ }
117
+ /**
118
+ * Classify the script that registered a tool. Returns null when the script
119
+ * URL is unknown or not http(s).
120
+ */
121
+ export function classifyRegistrar(registeredBy, pageUrl, platforms = []) {
122
+ const regSite = siteOf(registeredBy);
123
+ if (!registeredBy || !regSite)
124
+ return null;
125
+ const pageSite = siteOf(pageUrl);
126
+ if (!pageSite || regSite === pageSite)
127
+ return { kind: "first-party" };
128
+ const u = new URL(registeredBy);
129
+ const host = u.hostname.toLowerCase();
130
+ for (const p of PLATFORM_HOSTS) {
131
+ if (host === p.host && u.pathname.startsWith(p.pathPrefix) && platforms.includes(p.marker)) {
132
+ return { kind: "platform", platform: p.platform };
133
+ }
134
+ }
135
+ if (LIBRARY_CDN_HOSTS.includes(host)) {
136
+ const pkg = libraryPackage(registeredBy);
137
+ return pkg ? { kind: "library", package: pkg } : { kind: "library" };
138
+ }
139
+ return { kind: "third-party" };
140
+ }
141
+ //# sourceMappingURL=attribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attribution.js","sourceRoot":"","sources":["../../src/webmcp/attribution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import type { EnumerationResult } from "./types.js";
2
+ /** Playwright always passes CDPScreenshotNewSurface; a second --enable-features would override it. */
3
+ export declare const CHROMIUM_ARGS: string[];
4
+ export declare const UA_SUFFIX = "vskill-audit-webmcp (+https://verified-skill.com)";
5
+ export declare const PROBE_NAME = "vskill-webmcp-probe";
6
+ interface PwResponse {
7
+ headers(): Record<string, string>;
8
+ }
9
+ interface PwCdpSession {
10
+ send(method: string, params?: Record<string, unknown>): Promise<unknown>;
11
+ on(event: string, handler: (params: any) => void): void;
12
+ detach(): Promise<void>;
13
+ }
14
+ interface PwPage {
15
+ goto(url: string, opts: {
16
+ waitUntil: string;
17
+ timeout: number;
18
+ }): Promise<PwResponse | null>;
19
+ url(): string;
20
+ evaluate<T>(fn: (...args: any[]) => T | Promise<T>, arg?: unknown): Promise<T>;
21
+ waitForTimeout(ms: number): Promise<void>;
22
+ on(event: string, handler: (arg: any) => void): void;
23
+ close(): Promise<void>;
24
+ }
25
+ interface PwContext {
26
+ newPage(): Promise<PwPage>;
27
+ newCDPSession(page: PwPage): Promise<PwCdpSession>;
28
+ addInitScript(script: {
29
+ content: string;
30
+ }): Promise<void>;
31
+ on(event: string, handler: (arg: any) => void): void;
32
+ close(): Promise<void>;
33
+ }
34
+ export interface PwBrowser {
35
+ newContext(opts?: Record<string, unknown>): Promise<PwContext>;
36
+ close(): Promise<void>;
37
+ version(): string;
38
+ isConnected(): boolean;
39
+ }
40
+ interface PwChromium {
41
+ launch(opts: Record<string, unknown>): Promise<PwBrowser>;
42
+ executablePath(): string;
43
+ }
44
+ /**
45
+ * Import playwright-core, then playwright: first from vskill's own install,
46
+ * then from the current project (so `npm i -D playwright-core` works with npx).
47
+ * Returns null when neither resolves.
48
+ */
49
+ export declare function loadChromium(cwd?: string): Promise<PwChromium | null>;
50
+ export declare const PLAYWRIGHT_INSTALL_HINT: string;
51
+ /** Newest Chromium (headless shell preferred) in the usual Playwright cache dirs. */
52
+ export declare function findCachedChromium(dirs?: string[]): string | null;
53
+ /**
54
+ * Resolve the executable: explicit flag, then VSKILL_CHROMIUM, then
55
+ * Playwright's own pinned build, then the newest cached build.
56
+ * Returns undefined when Playwright's default should be used.
57
+ */
58
+ export declare function resolveExecutablePath(chromium: PwChromium, explicit?: string): string | undefined;
59
+ export declare function launchBrowser(opts?: {
60
+ executablePath?: string;
61
+ }): Promise<PwBrowser>;
62
+ /**
63
+ * Runs before any page script in every frame. Wraps registerTool to record
64
+ * the descriptor (minus execute) and the caller's stack. The page's own
65
+ * registerTool still runs unchanged.
66
+ */
67
+ export declare const INIT_SCRIPT = "(() => {\n const KEY = Symbol.for(\"vskill.webmcp\");\n if (window[KEY]) return;\n const log = [];\n try { Object.defineProperty(window, KEY, { value: log, enumerable: false }); } catch (e) { return; }\n // Crawl safety: the audit loads one page; popups (popunders, auto-opened tabs) never load.\n try { window.open = function open() { return null; }; } catch (e) {}\n let proto = null;\n try {\n proto = (typeof ModelContext !== \"undefined\" && ModelContext.prototype) ||\n (document.modelContext && Object.getPrototypeOf(document.modelContext));\n } catch (e) {}\n if (!proto || typeof proto.registerTool !== \"function\") return;\n const safe = (v) => { try { return v === undefined ? undefined : JSON.parse(JSON.stringify(v)); } catch (e) { return undefined; } };\n const str = (v) => (typeof v === \"string\" ? v : undefined);\n const orig = proto.registerTool;\n const wrapped = function registerTool(tool, options) {\n try {\n const t = tool || {};\n const rec = {\n name: String(t.name == null ? \"\" : t.name),\n title: str(t.title),\n description: str(t.description),\n inputSchema: typeof t.inputSchema === \"string\" ? t.inputSchema : safe(t.inputSchema),\n annotations: safe(t.annotations),\n exposedTo: safe((options && options.exposedTo) !== undefined ? options.exposedTo : t.exposedTo),\n stack: String(new Error().stack || \"\"),\n };\n log.push(rec);\n const signal = options && options.signal;\n if (signal && typeof signal.addEventListener === \"function\") {\n signal.addEventListener(\"abort\", () => log.push({ name: rec.name, unregistered: true }), { once: true });\n }\n } catch (e) {}\n return orig.apply(this, arguments);\n };\n try { Object.defineProperty(proto, \"registerTool\", { value: wrapped, writable: true, configurable: true }); } catch (e) {}\n if (typeof proto.unregisterTool === \"function\") {\n const origUn = proto.unregisterTool;\n try {\n Object.defineProperty(proto, \"unregisterTool\", {\n value: function unregisterTool(name) {\n try { log.push({ name: String(name), unregistered: true }); } catch (e) {}\n return origUn.apply(this, arguments);\n },\n writable: true,\n configurable: true,\n });\n } catch (e) {}\n }\n})();\n//# sourceURL=vskill-webmcp-probe.js";
68
+ export interface EnumerateOptions {
69
+ timeoutMs: number;
70
+ /** Max wait after load for registrations to stop (default 5000). */
71
+ settleMs?: number;
72
+ /** Quiet period that ends the settle wait early (default 1500). */
73
+ quietMs?: number;
74
+ userAgent?: string;
75
+ /** Aborting closes the browser context, which ends any pending page work. */
76
+ signal?: AbortSignal;
77
+ }
78
+ /** Browser UA with our suffix, read once per browser. */
79
+ export declare function userAgentFor(browser: PwBrowser): Promise<string>;
80
+ export declare function enumerateSite(browser: PwBrowser, url: string, opts: EnumerateOptions): Promise<EnumerationResult>;
81
+ export {};
@@ -0,0 +1,436 @@
1
+ // ---------------------------------------------------------------------------
2
+ // vskill audit-webmcp -- headless Chromium enumeration of WebMCP tools.
3
+ //
4
+ // Playwright is an optional peer: we import 'playwright-core' or 'playwright'
5
+ // at runtime and never add it as a dependency. Each URL loads once in its own
6
+ // browser context. We read tool descriptors only: no tool is invoked or
7
+ // executed, no form is submitted, nothing is clicked, downloads are refused,
8
+ // dialogs dismissed, popups closed and service workers blocked.
9
+ // ---------------------------------------------------------------------------
10
+ import { existsSync, readdirSync } from "node:fs";
11
+ import { createRequire } from "node:module";
12
+ import { homedir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { pathToFileURL } from "node:url";
15
+ import { mergeTools, scriptUrlFromStack } from "./merge.js";
16
+ /** Playwright always passes CDPScreenshotNewSurface; a second --enable-features would override it. */
17
+ export const CHROMIUM_ARGS = ["--enable-features=CDPScreenshotNewSurface,WebMCP"];
18
+ export const UA_SUFFIX = "vskill-audit-webmcp (+https://verified-skill.com)";
19
+ export const PROBE_NAME = "vskill-webmcp-probe";
20
+ /**
21
+ * Import playwright-core, then playwright: first from vskill's own install,
22
+ * then from the current project (so `npm i -D playwright-core` works with npx).
23
+ * Returns null when neither resolves.
24
+ */
25
+ export async function loadChromium(cwd = process.cwd()) {
26
+ const projectRequire = createRequire(join(cwd, "noop.js"));
27
+ for (const specifier of ["playwright-core", "playwright"]) {
28
+ const attempts = [
29
+ () => import(specifier),
30
+ () => import(pathToFileURL(projectRequire.resolve(specifier)).href),
31
+ ];
32
+ for (const attempt of attempts) {
33
+ try {
34
+ const mod = await attempt();
35
+ const chromium = mod.chromium ?? mod.default?.chromium;
36
+ if (chromium)
37
+ return chromium;
38
+ }
39
+ catch {
40
+ // try the next one
41
+ }
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+ export const PLAYWRIGHT_INSTALL_HINT = [
47
+ "audit-webmcp needs Playwright and a Chromium build. Install them with:",
48
+ " npm i -D playwright-core (in the project you run vskill from; or npm i -g alongside a global vskill)",
49
+ " npx playwright install chromium-headless-shell",
50
+ "Or point at an existing build with --executable-path <path> or VSKILL_CHROMIUM=<path>.",
51
+ ].join("\n");
52
+ // ---- Finding a Chromium build ----------------------------------------------
53
+ function browserCacheDirs() {
54
+ const dirs = [];
55
+ if (process.env.PLAYWRIGHT_BROWSERS_PATH && process.env.PLAYWRIGHT_BROWSERS_PATH !== "0") {
56
+ dirs.push(process.env.PLAYWRIGHT_BROWSERS_PATH);
57
+ }
58
+ const home = homedir();
59
+ dirs.push(join(home, "Library", "Caches", "ms-playwright"));
60
+ dirs.push(join(home, ".cache", "ms-playwright"));
61
+ if (process.env.LOCALAPPDATA)
62
+ dirs.push(join(process.env.LOCALAPPDATA, "ms-playwright"));
63
+ dirs.push("/opt/pw-browsers");
64
+ return dirs;
65
+ }
66
+ const BINARY_CANDIDATES = [
67
+ {
68
+ prefix: "chromium_headless_shell-",
69
+ paths: [
70
+ ["chrome-headless-shell-mac-arm64", "chrome-headless-shell"],
71
+ ["chrome-headless-shell-mac-x64", "chrome-headless-shell"],
72
+ ["chrome-headless-shell-linux64", "chrome-headless-shell"],
73
+ ["chrome-headless-shell-linux-arm64", "chrome-headless-shell"],
74
+ ["chrome-linux", "headless_shell"],
75
+ ["chrome-headless-shell-win64", "chrome-headless-shell.exe"],
76
+ ],
77
+ },
78
+ {
79
+ prefix: "chromium-",
80
+ paths: [
81
+ ["chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"],
82
+ ["chrome-mac", "Chromium.app", "Contents", "MacOS", "Chromium"],
83
+ ["chrome-linux64", "chrome"],
84
+ ["chrome-linux", "chrome"],
85
+ ["chrome-win64", "chrome.exe"],
86
+ ["chrome-win", "chrome.exe"],
87
+ ],
88
+ },
89
+ ];
90
+ /** Newest Chromium (headless shell preferred) in the usual Playwright cache dirs. */
91
+ export function findCachedChromium(dirs = browserCacheDirs()) {
92
+ for (const { prefix, paths } of BINARY_CANDIDATES) {
93
+ const found = [];
94
+ for (const dir of dirs) {
95
+ let entries;
96
+ try {
97
+ entries = readdirSync(dir);
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ for (const e of entries) {
103
+ if (!e.startsWith(prefix))
104
+ continue;
105
+ const rev = parseInt(e.slice(prefix.length), 10);
106
+ if (Number.isNaN(rev))
107
+ continue;
108
+ for (const segs of paths) {
109
+ const p = join(dir, e, ...segs);
110
+ if (existsSync(p))
111
+ found.push({ rev, path: p });
112
+ }
113
+ }
114
+ }
115
+ found.sort((a, b) => b.rev - a.rev);
116
+ if (found.length)
117
+ return found[0].path;
118
+ }
119
+ return null;
120
+ }
121
+ /**
122
+ * Resolve the executable: explicit flag, then VSKILL_CHROMIUM, then
123
+ * Playwright's own pinned build, then the newest cached build.
124
+ * Returns undefined when Playwright's default should be used.
125
+ */
126
+ export function resolveExecutablePath(chromium, explicit) {
127
+ if (explicit)
128
+ return explicit;
129
+ if (process.env.VSKILL_CHROMIUM)
130
+ return process.env.VSKILL_CHROMIUM;
131
+ try {
132
+ const def = chromium.executablePath();
133
+ if (def && existsSync(def))
134
+ return undefined;
135
+ }
136
+ catch {
137
+ // fall through
138
+ }
139
+ return findCachedChromium() ?? undefined;
140
+ }
141
+ export async function launchBrowser(opts = {}) {
142
+ const chromium = await loadChromium();
143
+ if (!chromium) {
144
+ const err = new Error(PLAYWRIGHT_INSTALL_HINT);
145
+ err.code = "VSKILL_NO_PLAYWRIGHT";
146
+ throw err;
147
+ }
148
+ const executablePath = resolveExecutablePath(chromium, opts.executablePath);
149
+ return chromium.launch({
150
+ headless: true,
151
+ args: CHROMIUM_ARGS,
152
+ ...(executablePath ? { executablePath } : {}),
153
+ });
154
+ }
155
+ // ---- In-page probe ---------------------------------------------------------
156
+ /**
157
+ * Runs before any page script in every frame. Wraps registerTool to record
158
+ * the descriptor (minus execute) and the caller's stack. The page's own
159
+ * registerTool still runs unchanged.
160
+ */
161
+ export const INIT_SCRIPT = `(() => {
162
+ const KEY = Symbol.for("vskill.webmcp");
163
+ if (window[KEY]) return;
164
+ const log = [];
165
+ try { Object.defineProperty(window, KEY, { value: log, enumerable: false }); } catch (e) { return; }
166
+ // Crawl safety: the audit loads one page; popups (popunders, auto-opened tabs) never load.
167
+ try { window.open = function open() { return null; }; } catch (e) {}
168
+ let proto = null;
169
+ try {
170
+ proto = (typeof ModelContext !== "undefined" && ModelContext.prototype) ||
171
+ (document.modelContext && Object.getPrototypeOf(document.modelContext));
172
+ } catch (e) {}
173
+ if (!proto || typeof proto.registerTool !== "function") return;
174
+ const safe = (v) => { try { return v === undefined ? undefined : JSON.parse(JSON.stringify(v)); } catch (e) { return undefined; } };
175
+ const str = (v) => (typeof v === "string" ? v : undefined);
176
+ const orig = proto.registerTool;
177
+ const wrapped = function registerTool(tool, options) {
178
+ try {
179
+ const t = tool || {};
180
+ const rec = {
181
+ name: String(t.name == null ? "" : t.name),
182
+ title: str(t.title),
183
+ description: str(t.description),
184
+ inputSchema: typeof t.inputSchema === "string" ? t.inputSchema : safe(t.inputSchema),
185
+ annotations: safe(t.annotations),
186
+ exposedTo: safe((options && options.exposedTo) !== undefined ? options.exposedTo : t.exposedTo),
187
+ stack: String(new Error().stack || ""),
188
+ };
189
+ log.push(rec);
190
+ const signal = options && options.signal;
191
+ if (signal && typeof signal.addEventListener === "function") {
192
+ signal.addEventListener("abort", () => log.push({ name: rec.name, unregistered: true }), { once: true });
193
+ }
194
+ } catch (e) {}
195
+ return orig.apply(this, arguments);
196
+ };
197
+ try { Object.defineProperty(proto, "registerTool", { value: wrapped, writable: true, configurable: true }); } catch (e) {}
198
+ if (typeof proto.unregisterTool === "function") {
199
+ const origUn = proto.unregisterTool;
200
+ try {
201
+ Object.defineProperty(proto, "unregisterTool", {
202
+ value: function unregisterTool(name) {
203
+ try { log.push({ name: String(name), unregistered: true }); } catch (e) {}
204
+ return origUn.apply(this, arguments);
205
+ },
206
+ writable: true,
207
+ configurable: true,
208
+ });
209
+ } catch (e) {}
210
+ }
211
+ })();
212
+ //# sourceURL=${PROBE_NAME}.js`;
213
+ /** Evaluated in the page after it settles. Reads, never executes. */
214
+ function snapshotInPage() {
215
+ const w = window;
216
+ const d = document;
217
+ const mc = d.modelContext;
218
+ const out = {
219
+ documentModelContext: !!mc,
220
+ navigatorModelContext: "modelContext" in navigator,
221
+ captured: [],
222
+ listed: null,
223
+ platforms: [],
224
+ };
225
+ // Hosting-platform markers (attribution.ts PLATFORM_HOSTS). Shopify storefronts
226
+ // set window.Shopify.shop to the store's myshopify.com name and emit these metas.
227
+ try {
228
+ const shop = w.Shopify;
229
+ const shopifyMeta = d.querySelector('meta[name="shopify-checkout-api-token"], meta[name="shopify-digital-wallet"]');
230
+ if ((shop && typeof shop === "object" && typeof shop.shop === "string") || shopifyMeta || /\.myshopify\.com$/i.test(location.hostname)) {
231
+ out.platforms.push("shopify");
232
+ }
233
+ }
234
+ catch {
235
+ // markers are best effort
236
+ }
237
+ const log = w[Symbol.for("vskill.webmcp")];
238
+ if (Array.isArray(log))
239
+ out.captured = log.slice();
240
+ if (!mc || typeof mc.getTools !== "function")
241
+ return Promise.resolve(out);
242
+ const clone = (v) => {
243
+ try {
244
+ return v === undefined ? undefined : JSON.parse(JSON.stringify(v));
245
+ }
246
+ catch {
247
+ return undefined;
248
+ }
249
+ };
250
+ return Promise.race([
251
+ Promise.resolve(mc.getTools()),
252
+ new Promise((_, reject) => setTimeout(() => reject(new Error("getTools() timed out")), 3000)),
253
+ ])
254
+ .then((tools) => {
255
+ out.listed = Array.from(tools || []).map((t) => ({
256
+ name: String(t.name),
257
+ title: typeof t.title === "string" ? t.title : undefined,
258
+ description: typeof t.description === "string" ? t.description : undefined,
259
+ inputSchema: typeof t.inputSchema === "string" ? t.inputSchema : clone(t.inputSchema),
260
+ annotations: clone(t.annotations),
261
+ origin: typeof t.origin === "string" ? t.origin : undefined,
262
+ exposedTo: clone(t.exposedTo),
263
+ }));
264
+ return out;
265
+ })
266
+ .catch((e) => {
267
+ out.getToolsError = String(e);
268
+ return out;
269
+ });
270
+ }
271
+ /** Reject when `p` takes longer than `ms` (a page stuck in a busy loop never answers evaluate). */
272
+ function withTimeout(p, ms, what) {
273
+ let timer;
274
+ return Promise.race([
275
+ p,
276
+ new Promise((_, reject) => {
277
+ timer = setTimeout(() => reject(new Error(`${what} timed out after ${ms}ms`)), ms);
278
+ }),
279
+ ]).finally(() => clearTimeout(timer));
280
+ }
281
+ function firstLine(e) {
282
+ const msg = e instanceof Error ? e.message : String(e);
283
+ return msg.split("\n")[0].replace(/^page\.goto:\s*/, "").trim();
284
+ }
285
+ function cdpToolList(params) {
286
+ if (Array.isArray(params?.tools))
287
+ return params.tools;
288
+ if (params?.tool)
289
+ return [params.tool];
290
+ if (params?.name)
291
+ return [params];
292
+ return [];
293
+ }
294
+ /** Browser UA with our suffix, read once per browser. */
295
+ export async function userAgentFor(browser) {
296
+ const ctx = await browser.newContext();
297
+ try {
298
+ const page = await ctx.newPage();
299
+ const ua = await page.evaluate(() => navigator.userAgent);
300
+ return `${ua} ${UA_SUFFIX}`;
301
+ }
302
+ finally {
303
+ await ctx.close();
304
+ }
305
+ }
306
+ export async function enumerateSite(browser, url, opts) {
307
+ const settleMs = opts.settleMs ?? 5000;
308
+ const quietMs = opts.quietMs ?? 1500;
309
+ const result = {
310
+ url,
311
+ finalUrl: null,
312
+ documentModelContext: false,
313
+ navigatorModelContext: false,
314
+ permissionsPolicy: null,
315
+ platforms: [],
316
+ tools: [],
317
+ };
318
+ if (opts.signal?.aborted) {
319
+ result.error = "aborted";
320
+ return result;
321
+ }
322
+ const ctx = await browser.newContext({
323
+ acceptDownloads: false,
324
+ javaScriptEnabled: true,
325
+ serviceWorkers: "block",
326
+ ...(opts.userAgent ? { userAgent: opts.userAgent } : {}),
327
+ });
328
+ const onAbort = () => {
329
+ ctx.close().catch(() => { });
330
+ };
331
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
332
+ try {
333
+ await ctx.addInitScript({ content: INIT_SCRIPT });
334
+ const page = await ctx.newPage();
335
+ page.on("dialog", (d) => {
336
+ d.dismiss().catch(() => { });
337
+ });
338
+ // window.open popups would load more pages we never asked for: close them.
339
+ ctx.on("page", (p) => {
340
+ if (p !== page)
341
+ p.close().catch(() => { });
342
+ });
343
+ const cdpEvents = [];
344
+ try {
345
+ const cdp = await ctx.newCDPSession(page);
346
+ cdp.on("WebMCP.toolsAdded", (p) => {
347
+ for (const t of cdpToolList(p))
348
+ if (t?.name)
349
+ cdpEvents.push({ ...t, name: String(t.name) });
350
+ });
351
+ cdp.on("WebMCP.toolsRemoved", (p) => {
352
+ const names = Array.isArray(p?.names) ? p.names : cdpToolList(p).map((t) => t?.name ?? t);
353
+ for (const n of names)
354
+ if (typeof n === "string")
355
+ cdpEvents.push({ name: n, removed: true });
356
+ });
357
+ await cdp.send("WebMCP.enable");
358
+ }
359
+ catch {
360
+ // Older Chromium without the WebMCP domain: rely on the other sources.
361
+ }
362
+ try {
363
+ const response = await page.goto(url, { waitUntil: "load", timeout: opts.timeoutMs });
364
+ const headers = response?.headers() ?? {};
365
+ result.permissionsPolicy = headers["permissions-policy"] ?? null;
366
+ }
367
+ catch (e) {
368
+ const msg = firstLine(e);
369
+ if (!/timeout/i.test(msg) || page.url() === "about:blank") {
370
+ result.error = msg;
371
+ result.finalUrl = page.url() === "about:blank" ? null : page.url();
372
+ return result;
373
+ }
374
+ result.error = `${msg} (partial: tools registered before the timeout are included)`;
375
+ }
376
+ // Wait for registrations to stop: at most settleMs, done early after quietMs without change.
377
+ const countNow = async () => {
378
+ const captured = await withTimeout(page.evaluate(() => {
379
+ const log = window[Symbol.for("vskill.webmcp")];
380
+ return Array.isArray(log) ? log.length : 0;
381
+ }), 2000, "page.evaluate").catch(() => 0);
382
+ return captured + cdpEvents.length;
383
+ };
384
+ const start = Date.now();
385
+ let last = await countNow();
386
+ let lastChange = Date.now();
387
+ while (Date.now() - start < settleMs) {
388
+ await page.waitForTimeout(250);
389
+ const n = await countNow();
390
+ if (n !== last) {
391
+ last = n;
392
+ lastChange = Date.now();
393
+ }
394
+ else if (Date.now() - lastChange >= quietMs) {
395
+ break;
396
+ }
397
+ }
398
+ result.finalUrl = page.url();
399
+ const readSnapshot = () => withTimeout(page.evaluate(snapshotInPage), 10_000, "reading tools from the page");
400
+ let snap;
401
+ try {
402
+ snap = await readSnapshot();
403
+ }
404
+ catch (e) {
405
+ // The page navigated itself (client-side redirect) mid-read: read the new document once more.
406
+ if (!/context was destroyed|navigat/i.test(String(e)))
407
+ throw e;
408
+ await page.waitForTimeout(1000);
409
+ snap = await readSnapshot();
410
+ }
411
+ result.finalUrl = page.url();
412
+ result.documentModelContext = snap.documentModelContext;
413
+ result.navigatorModelContext = snap.navigatorModelContext;
414
+ result.platforms = snap.platforms;
415
+ const captured = snap.captured.map(({ stack, ...rest }) => ({
416
+ ...rest,
417
+ scriptUrl: rest.unregistered ? undefined : scriptUrlFromStack(stack, PROBE_NAME),
418
+ }));
419
+ result.tools = mergeTools({
420
+ pageUrl: result.finalUrl,
421
+ captured,
422
+ cdp: cdpEvents,
423
+ listed: snap.listed,
424
+ platforms: snap.platforms,
425
+ });
426
+ }
427
+ catch (e) {
428
+ result.error = result.error ?? firstLine(e);
429
+ }
430
+ finally {
431
+ opts.signal?.removeEventListener("abort", onAbort);
432
+ await ctx.close().catch(() => { });
433
+ }
434
+ return result;
435
+ }
436
+ //# sourceMappingURL=enumerate.js.map