@wordbricks/playwright-mcp 0.1.20 → 0.1.23

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 (89) hide show
  1. package/cli-wrapper.js +15 -14
  2. package/cli.js +1 -1
  3. package/config.d.ts +11 -6
  4. package/index.d.ts +7 -5
  5. package/index.js +1 -1
  6. package/package.json +34 -57
  7. package/LICENSE +0 -202
  8. package/lib/browserContextFactory.js +0 -326
  9. package/lib/browserServerBackend.js +0 -84
  10. package/lib/config.js +0 -286
  11. package/lib/context.js +0 -309
  12. package/lib/extension/cdpRelay.js +0 -346
  13. package/lib/extension/extensionContextFactory.js +0 -56
  14. package/lib/frameworkPatterns.js +0 -35
  15. package/lib/hooks/antiBotDetectionHook.js +0 -171
  16. package/lib/hooks/core.js +0 -144
  17. package/lib/hooks/eventConsumer.js +0 -52
  18. package/lib/hooks/events.js +0 -42
  19. package/lib/hooks/formatToolCallEvent.js +0 -16
  20. package/lib/hooks/frameworkStateHook.js +0 -182
  21. package/lib/hooks/grouping.js +0 -72
  22. package/lib/hooks/jsonLdDetectionHook.js +0 -175
  23. package/lib/hooks/networkFilters.js +0 -82
  24. package/lib/hooks/networkSetup.js +0 -59
  25. package/lib/hooks/networkTrackingHook.js +0 -67
  26. package/lib/hooks/pageHeightHook.js +0 -75
  27. package/lib/hooks/registry.js +0 -42
  28. package/lib/hooks/requireTabHook.js +0 -26
  29. package/lib/hooks/schema.js +0 -89
  30. package/lib/hooks/waitHook.js +0 -33
  31. package/lib/index.js +0 -39
  32. package/lib/mcp/inProcessTransport.js +0 -72
  33. package/lib/mcp/proxyBackend.js +0 -115
  34. package/lib/mcp/server.js +0 -86
  35. package/lib/mcp/tool.js +0 -38
  36. package/lib/mcp/transport.js +0 -181
  37. package/lib/playwrightTransformer.js +0 -497
  38. package/lib/program.js +0 -110
  39. package/lib/response.js +0 -186
  40. package/lib/sessionLog.js +0 -121
  41. package/lib/tab.js +0 -249
  42. package/lib/tools/common.js +0 -55
  43. package/lib/tools/console.js +0 -33
  44. package/lib/tools/dialogs.js +0 -47
  45. package/lib/tools/evaluate.js +0 -53
  46. package/lib/tools/extractFrameworkState.js +0 -214
  47. package/lib/tools/files.js +0 -45
  48. package/lib/tools/form.js +0 -57
  49. package/lib/tools/getSnapshot.js +0 -37
  50. package/lib/tools/getVisibleHtml.js +0 -52
  51. package/lib/tools/install.js +0 -51
  52. package/lib/tools/keyboard.js +0 -78
  53. package/lib/tools/mouse.js +0 -99
  54. package/lib/tools/navigate.js +0 -70
  55. package/lib/tools/network.js +0 -123
  56. package/lib/tools/networkDetail.js +0 -229
  57. package/lib/tools/networkSearch/bodySearch.js +0 -147
  58. package/lib/tools/networkSearch/grouping.js +0 -28
  59. package/lib/tools/networkSearch/helpers.js +0 -32
  60. package/lib/tools/networkSearch/searchHtml.js +0 -67
  61. package/lib/tools/networkSearch/types.js +0 -1
  62. package/lib/tools/networkSearch/urlSearch.js +0 -82
  63. package/lib/tools/networkSearch.js +0 -268
  64. package/lib/tools/pdf.js +0 -40
  65. package/lib/tools/repl.js +0 -402
  66. package/lib/tools/screenshot.js +0 -79
  67. package/lib/tools/scroll.js +0 -126
  68. package/lib/tools/snapshot.js +0 -144
  69. package/lib/tools/tabs.js +0 -59
  70. package/lib/tools/tool.js +0 -33
  71. package/lib/tools/utils.js +0 -74
  72. package/lib/tools/wait.js +0 -55
  73. package/lib/tools.js +0 -67
  74. package/lib/utils/adBlockFilter.js +0 -87
  75. package/lib/utils/codegen.js +0 -51
  76. package/lib/utils/extensionPath.js +0 -10
  77. package/lib/utils/fileUtils.js +0 -36
  78. package/lib/utils/graphql.js +0 -258
  79. package/lib/utils/guid.js +0 -22
  80. package/lib/utils/httpServer.js +0 -39
  81. package/lib/utils/log.js +0 -21
  82. package/lib/utils/manualPromise.js +0 -111
  83. package/lib/utils/networkFormat.js +0 -12
  84. package/lib/utils/package.js +0 -20
  85. package/lib/utils/result.js +0 -2
  86. package/lib/utils/sanitizeHtml.js +0 -98
  87. package/lib/utils/truncate.js +0 -103
  88. package/lib/utils/withTimeout.js +0 -7
  89. package/src/index.ts +0 -50
@@ -1,56 +0,0 @@
1
- /**
2
- * Copyright (c) Microsoft Corporation.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
- import debug from 'debug';
17
- import * as playwright from 'playwright-core';
18
- import { startHttpServer } from '../utils/httpServer.js';
19
- import { CDPRelayServer } from './cdpRelay.js';
20
- const debugLogger = debug('pw:mcp:relay');
21
- export class ExtensionContextFactory {
22
- name = 'extension';
23
- description = 'Connect to a browser using the Playwright MCP extension';
24
- _browserChannel;
25
- _userDataDir;
26
- constructor(browserChannel, userDataDir) {
27
- this._browserChannel = browserChannel;
28
- this._userDataDir = userDataDir;
29
- }
30
- async createContext(clientInfo, abortSignal) {
31
- const browser = await this._obtainBrowser(clientInfo, abortSignal);
32
- return {
33
- browserContext: browser.contexts()[0],
34
- close: async () => {
35
- debugLogger('close() called for browser context');
36
- await browser.close();
37
- }
38
- };
39
- }
40
- async _obtainBrowser(clientInfo, abortSignal) {
41
- const relay = await this._startRelay(abortSignal);
42
- await relay.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal);
43
- return await playwright.chromium.connectOverCDP(relay.cdpEndpoint());
44
- }
45
- async _startRelay(abortSignal) {
46
- const httpServer = await startHttpServer({});
47
- if (abortSignal.aborted) {
48
- httpServer.close();
49
- throw new Error(abortSignal.reason);
50
- }
51
- const cdpRelayServer = new CDPRelayServer(httpServer, this._browserChannel, this._userDataDir);
52
- abortSignal.addEventListener('abort', () => cdpRelayServer.stop());
53
- debugLogger(`CDP relay server started, extension endpoint: ${cdpRelayServer.extensionEndpoint()}.`);
54
- return cdpRelayServer;
55
- }
56
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Shared framework state patterns used by framework state detection hook
3
- */
4
- export const FRAMEWORK_STATE_PATTERNS = [
5
- // React/Next.js
6
- '__NEXT_DATA__',
7
- '__reactServerState',
8
- // Remix
9
- '__remixContext',
10
- '__remixManifest',
11
- '__remixRouteModules',
12
- // Apollo GraphQL
13
- '__APOLLO_STATE__',
14
- '__APOLLO_CLIENT__',
15
- // Redux/State Management
16
- '__PRELOADED_STATE__',
17
- '__INITIAL_STATE__',
18
- '__REDUX_STATE__',
19
- // Vue/Nuxt
20
- '__NUXT__',
21
- // Gatsby
22
- '___gatsby',
23
- '___loader',
24
- // Generic SSR
25
- '__SSR_DATA__',
26
- '__APP_STATE__',
27
- '__SERVER_STATE__',
28
- // Others
29
- '__QWIK_STATE__',
30
- '__SVELTE__',
31
- '__ANGULAR__',
32
- '__SOLID__',
33
- '__ASTRO_DATA__',
34
- ];
35
- export const MAX_DISPLAY_ITEMS = 5;
@@ -1,171 +0,0 @@
1
- import ms from 'ms';
2
- import { Ok } from '../utils/result.js';
3
- import { hookNameSchema } from './schema.js';
4
- import { getEventsAfter, isEventType, trackEvent } from './events.js';
5
- const RESOLUTION_WAIT_MS = ms('10s');
6
- const lastProcessedEventIdByContext = new WeakMap();
7
- const detectedProvidersByContext = new WeakMap();
8
- const isLikelyResolved = async (ctx) => {
9
- if (!ctx.tab?.page)
10
- return false;
11
- const host = ctx.tab.page.url();
12
- if (host.includes('challenges.cloudflare.com'))
13
- return false;
14
- return ctx.tab.page.evaluate(() => {
15
- const challengeSelectors = [
16
- '#challenge-stage',
17
- '#cf-challenge-running',
18
- 'iframe[src*="turnstile"]',
19
- 'form[action*="/cdn-cgi/challenge-platform"]',
20
- '[data-cf-challenge]'
21
- ];
22
- const hasChallengeDom = challengeSelectors.some(selector => document.querySelector(selector));
23
- if (hasChallengeDom)
24
- return false;
25
- const title = document.title.toLowerCase();
26
- const bodyText = (document.body?.innerText || '').slice(0, 2000).toLowerCase();
27
- if (title.includes('just a moment') || bodyText.includes('just a moment'))
28
- return false;
29
- if (title.includes('checking your browser') || bodyText.includes('checking your browser'))
30
- return false;
31
- return true;
32
- });
33
- };
34
- const getDetectedProviders = (context) => {
35
- const detectedProviders = detectedProvidersByContext.get(context);
36
- if (detectedProviders)
37
- return detectedProviders;
38
- const newSet = new Set();
39
- detectedProvidersByContext.set(context, newSet);
40
- return newSet;
41
- };
42
- const updateLastProcessedEventId = (context, events) => {
43
- const lastEvent = events[events.length - 1];
44
- if (!lastEvent)
45
- return;
46
- lastProcessedEventIdByContext.set(context, lastEvent.id);
47
- };
48
- const isStatusOk = (status) => status >= 200 && status < 400;
49
- const providerConfigs = [
50
- {
51
- provider: 'cloudflare-turnstile',
52
- match: event => isStatusOk(event.data.status) &&
53
- event.data.url.includes('challenges.cloudflare.com') &&
54
- event.data.url.includes('/turnstile/'),
55
- },
56
- {
57
- provider: 'aws-waf',
58
- match: event => isStatusOk(event.data.status) &&
59
- event.data.method.toUpperCase() === 'POST' &&
60
- event.data.url.includes('.awswaf.com') &&
61
- event.data.url.includes('/telemetry'),
62
- },
63
- ];
64
- export const getAntiBotProviderConfigs = () => providerConfigs;
65
- const waitForResolution = async (ctx) => {
66
- const start = Date.now();
67
- if (!ctx.tab?.page)
68
- return { resolved: false, waitedMs: 0 };
69
- await ctx.tab.page.waitForTimeout(RESOLUTION_WAIT_MS);
70
- const resolved = await isLikelyResolved(ctx);
71
- return { resolved, waitedMs: Date.now() - start };
72
- };
73
- const detectAntiBot = (ctx, events) => {
74
- const detectedProviders = getDetectedProviders(ctx.context);
75
- const networkEvents = events.filter(isEventType('network-request'));
76
- return providerConfigs.reduce((acc, config) => {
77
- if (detectedProviders.has(config.provider))
78
- return acc;
79
- const match = networkEvents.find(config.match);
80
- if (!match)
81
- return acc;
82
- detectedProviders.add(config.provider);
83
- return {
84
- hits: [
85
- ...acc.hits,
86
- {
87
- provider: config.provider,
88
- url: match.data.url,
89
- status: match.data.status,
90
- }
91
- ],
92
- };
93
- }, { hits: [] });
94
- };
95
- export const antiBotDetectionPreHook = {
96
- name: hookNameSchema.enum['anti-bot-detection-pre'],
97
- handler: async (ctx) => {
98
- if (lastProcessedEventIdByContext.has(ctx.context))
99
- return Ok(undefined);
100
- if (typeof ctx.eventStore.lastSeenEventId === 'number')
101
- lastProcessedEventIdByContext.set(ctx.context, ctx.eventStore.lastSeenEventId);
102
- return Ok(undefined);
103
- },
104
- };
105
- export const antiBotDetectionPostHook = {
106
- name: hookNameSchema.enum['anti-bot-detection-post'],
107
- handler: async (ctx) => {
108
- const newEvents = getEventsAfter(ctx.eventStore, lastProcessedEventIdByContext.get(ctx.context));
109
- if (newEvents.length === 0)
110
- return Ok(undefined);
111
- const detection = detectAntiBot(ctx, newEvents);
112
- if (detection.hits.length > 0) {
113
- detection.hits.forEach(hit => {
114
- trackEvent(ctx.context, {
115
- type: 'anti-bot',
116
- data: {
117
- provider: hit.provider,
118
- detectionMethod: 'network-request',
119
- url: hit.url,
120
- status: hit.status,
121
- action: 'detected',
122
- waitMs: RESOLUTION_WAIT_MS,
123
- },
124
- });
125
- });
126
- const waitResult = await waitForResolution(ctx);
127
- detection.hits.forEach(hit => {
128
- trackEvent(ctx.context, {
129
- type: 'anti-bot',
130
- data: {
131
- provider: hit.provider,
132
- detectionMethod: 'network-request',
133
- url: hit.url,
134
- status: hit.status,
135
- action: waitResult.resolved ? 'resolved' : 'still-blocked',
136
- waitMs: waitResult.waitedMs,
137
- },
138
- });
139
- });
140
- }
141
- updateLastProcessedEventId(ctx.context, newEvents);
142
- return Ok(undefined);
143
- },
144
- };
145
- export const antiBotDetectionHooks = {
146
- pre: antiBotDetectionPreHook,
147
- post: antiBotDetectionPostHook,
148
- };
149
- export const formatAntiBotEvent = (event) => {
150
- if (event.data.action === 'still-blocked') {
151
- const waitSeconds = event.data.waitMs ? Math.round(event.data.waitMs / 1000) : 0;
152
- if (event.data.provider === 'cloudflare-turnstile')
153
- return `Anti-bot still active: Cloudflare Turnstile after ${waitSeconds || '<1'}s wait`;
154
- if (event.data.provider === 'aws-waf')
155
- return `Anti-bot still active: AWS WAF after ${waitSeconds || '<1'}s wait`;
156
- return 'Anti-bot mechanism still active';
157
- }
158
- if (event.data.action === 'resolved') {
159
- const waitSeconds = event.data.waitMs ? Math.round(event.data.waitMs / 1000) : 0;
160
- if (event.data.provider === 'cloudflare-turnstile')
161
- return `Anti-bot resolved: Cloudflare Turnstile after ${waitSeconds || '<1'}s wait`;
162
- if (event.data.provider === 'aws-waf')
163
- return `Anti-bot resolved: AWS WAF after ${waitSeconds || '<1'}s wait`;
164
- return 'Anti-bot mechanism resolved';
165
- }
166
- if (event.data.provider === 'cloudflare-turnstile')
167
- return `Anti-bot detected: Cloudflare Turnstile (${event.data.status})`;
168
- if (event.data.provider === 'aws-waf')
169
- return `Anti-bot detected: AWS WAF telemetry request (${event.data.status})`;
170
- return 'Anti-bot mechanism detected';
171
- };
package/lib/hooks/core.js DELETED
@@ -1,144 +0,0 @@
1
- import { reduce } from '@fxts/core';
2
- import { getEventStore, trackEvent } from './events.js';
3
- import { consumeEvents } from './eventConsumer.js';
4
- import { toolNameSchema } from './schema.js';
5
- export { Ok, Err } from '../utils/result.js';
6
- export const runHook = async (hook, ctx) => {
7
- const result = await hook.handler(ctx);
8
- if (!result.ok)
9
- throw result.error;
10
- return ctx;
11
- };
12
- export const hookRegistryMap = new WeakMap();
13
- export const createHookRegistry = () => ({
14
- tools: new Map(),
15
- });
16
- export const setToolHooks = (registry, toolHooks) => ({
17
- tools: new Map([...registry.tools, [toolHooks.toolName, toolHooks]]),
18
- });
19
- export const getToolHooks = (registry, toolName) => {
20
- return registry.tools.get(toolName);
21
- };
22
- export const wrapToolWithHooks = (tool, registry) => {
23
- const parsedName = toolNameSchema.safeParse(tool.schema.name);
24
- if (!parsedName.success)
25
- return tool; // Tool name not in our schema, don't apply hooks
26
- // NOTE: This means tool call events won't be tracked for tools not in toolNameSchema.
27
- // All tools exposed by this MCP server should be added to the schema.
28
- const toolName = parsedName.data;
29
- const toolHooks = getToolHooks(registry, toolName);
30
- // Even if no hooks configured, we still need to consume events and track tool calls
31
- if (!toolHooks || (toolHooks.preHooks.length === 0 && toolHooks.postHooks.length === 0)) {
32
- return {
33
- ...tool,
34
- handle: async (context, params, response) => {
35
- const eventStore = getEventStore(context);
36
- // Consume pre-tool events
37
- consumeEvents(context, eventStore, response);
38
- // Track tool execution
39
- const startTime = Date.now();
40
- let success = true;
41
- try {
42
- await tool.handle(context, params, response);
43
- }
44
- catch (error) {
45
- success = false;
46
- throw error;
47
- }
48
- finally {
49
- // Record tool call completion
50
- const executionTime = Date.now() - startTime;
51
- trackEvent(context, {
52
- type: 'tool-call',
53
- data: {
54
- toolName,
55
- params: params,
56
- executionTime,
57
- success,
58
- },
59
- timestamp: startTime,
60
- });
61
- // Consume post-tool events
62
- consumeEvents(context, eventStore, response);
63
- }
64
- },
65
- };
66
- }
67
- return {
68
- ...tool,
69
- handle: async (context, params, response) => {
70
- const eventStore = getEventStore(context);
71
- // Consume pre-tool events
72
- consumeEvents(context, eventStore, response);
73
- // Track tool execution
74
- const startTime = Date.now();
75
- // Run pre-hooks
76
- const hookContext = {
77
- context,
78
- tab: context.currentTab(),
79
- params,
80
- toolName,
81
- response,
82
- eventStore,
83
- };
84
- try {
85
- await reduce(async (ctx, hook) => runHook(hook, await ctx), Promise.resolve(hookContext), toolHooks.preHooks);
86
- }
87
- catch (error) {
88
- // Pre-hook already handled messaging (e.g., require-tab pre-hook sets tabs section)
89
- // Avoid adding a duplicate error line in the Result section.
90
- return;
91
- }
92
- // Run original tool
93
- let success = true;
94
- try {
95
- await tool.handle(context, params, response);
96
- }
97
- catch (error) {
98
- success = false;
99
- throw error;
100
- }
101
- finally {
102
- // Record tool call completion
103
- const executionTime = Date.now() - startTime;
104
- trackEvent(context, {
105
- type: 'tool-call',
106
- data: {
107
- toolName,
108
- params: params,
109
- executionTime,
110
- success,
111
- },
112
- timestamp: startTime,
113
- });
114
- }
115
- // Run post-hooks
116
- const postHookContext = {
117
- context,
118
- tab: context.currentTab(),
119
- params,
120
- toolName,
121
- response,
122
- eventStore: getEventStore(context),
123
- };
124
- try {
125
- await reduce(async (ctx, hook) => runHook(hook, await ctx), Promise.resolve(postHookContext), toolHooks.postHooks);
126
- }
127
- catch (error) {
128
- response.addError(error instanceof Error ? error.message : 'Post-hook failed');
129
- }
130
- // Consume post-tool events
131
- consumeEvents(context, eventStore, response);
132
- },
133
- };
134
- };
135
- export const getHookRegistry = (context) => {
136
- const registry = hookRegistryMap.get(context);
137
- return registry || createHookRegistry();
138
- };
139
- export const applyHooksToTools = (tools, context) => {
140
- const registry = getHookRegistry(context);
141
- if (registry.tools.size === 0)
142
- return tools;
143
- return tools.map(tool => wrapToolWithHooks(tool, registry));
144
- };
@@ -1,52 +0,0 @@
1
- import { getEventsAfter, isEventType, updateLastSeenId } from './events.js';
2
- import { formatWaitEvent } from './waitHook.js';
3
- import { formatPageHeightEvent } from './pageHeightHook.js';
4
- import { formatNetworkEvent } from './networkTrackingHook.js';
5
- import { planGroupedMessages } from './grouping.js';
6
- import { formatToolCallEvent } from './formatToolCallEvent.js';
7
- import { formatFrameworkStateEvent } from './frameworkStateHook.js';
8
- import { formatJsonLdEvent } from './jsonLdDetectionHook.js';
9
- import { formatAntiBotEvent, getAntiBotProviderConfigs } from './antiBotDetectionHook.js';
10
- import { isAntiBotUrl } from './networkFilters.js';
11
- const eventFormatters = {
12
- 'wait': formatWaitEvent,
13
- 'page-height-change': formatPageHeightEvent,
14
- 'network-request': formatNetworkEvent,
15
- 'tool-call': formatToolCallEvent,
16
- 'framework-state': formatFrameworkStateEvent,
17
- 'json-ld': formatJsonLdEvent,
18
- 'anti-bot': formatAntiBotEvent,
19
- };
20
- const formatEvent = (event) => {
21
- const formatter = eventFormatters[event.type];
22
- return formatter(event);
23
- };
24
- const consumeEvent = (event, response, plan) => {
25
- if (plan.skipIds.has(event.id))
26
- return;
27
- const replacement = plan.replacementById.get(event.id);
28
- const formattedMessage = replacement ?? formatEvent(event);
29
- response.addEvent(`[${event.id}] ${formattedMessage}`);
30
- };
31
- const shouldHideEvent = (event) => {
32
- const isNetworkRequest = isEventType('network-request');
33
- if (!isNetworkRequest(event))
34
- return false;
35
- if (isAntiBotUrl(event.data.url))
36
- return true;
37
- const configs = getAntiBotProviderConfigs().filter(config => config.provider === 'cloudflare-turnstile');
38
- return configs.some(config => config.match(event));
39
- };
40
- export const consumeEvents = (context, eventStore, response) => {
41
- const unconsumedEvents = getEventsAfter(eventStore, eventStore.lastSeenEventId);
42
- if (unconsumedEvents.length === 0)
43
- return;
44
- const visibleEvents = unconsumedEvents.filter(event => !shouldHideEvent(event));
45
- const plan = planGroupedMessages(visibleEvents);
46
- // Consume all events in chronological order
47
- for (const event of visibleEvents)
48
- consumeEvent(event, response, plan);
49
- // Update last seen event ID
50
- const latestEvent = unconsumedEvents[unconsumedEvents.length - 1];
51
- updateLastSeenId(context, latestEvent.id);
52
- };
@@ -1,42 +0,0 @@
1
- import { pipe, filter, toArray } from '@fxts/core';
2
- export const isEventType = (type) => (event) => event.type === type;
3
- export const createEventStore = () => ({
4
- events: new Map(),
5
- lastSeenEventId: undefined,
6
- nextEventId: 1,
7
- });
8
- export const trackEvent = (context, params) => {
9
- const store = getEventStore(context);
10
- const eventId = store.nextEventId++;
11
- const event = {
12
- id: eventId,
13
- type: params.type,
14
- data: params.data,
15
- timestamp: params.timestamp ?? Date.now()
16
- };
17
- store.events.set(eventId, event);
18
- return eventId;
19
- };
20
- export const updateLastSeenId = (context, eventId) => {
21
- const store = getEventStore(context);
22
- store.lastSeenEventId = eventId;
23
- return context;
24
- };
25
- export const getEventsAfter = (store, afterEventId) => {
26
- if (!afterEventId) {
27
- return pipe(store.events.values(), toArray);
28
- }
29
- return pipe(store.events.values(), filter(event => event.id > afterEventId), toArray);
30
- };
31
- const eventStoreMap = new WeakMap();
32
- export const getEventStore = (context) => {
33
- let store = eventStoreMap.get(context);
34
- if (!store) {
35
- store = createEventStore();
36
- eventStoreMap.set(context, store);
37
- }
38
- return store;
39
- };
40
- export const setEventStore = (context, store) => {
41
- eventStoreMap.set(context, store);
42
- };
@@ -1,16 +0,0 @@
1
- export const formatToolCallEvent = (event) => {
2
- const { toolName, params, executionTime, success } = event.data;
3
- // Format parameters (truncate if too long)
4
- const paramStr = params && Object.keys(params).length > 0
5
- ? ` with params: ${JSON.stringify(params, null, 0).slice(0, 100)}`
6
- : '';
7
- // Format execution time if available
8
- const timeStr = executionTime !== undefined
9
- ? ` (${executionTime}ms)`
10
- : '';
11
- // Format success status if available
12
- const statusStr = success !== undefined
13
- ? success ? ' ✓' : ' ✗'
14
- : '';
15
- return `Tool ${toolName}${paramStr}${timeStr}${statusStr}`;
16
- };