@cotestdev/mcp_playwright 0.0.35 → 0.0.37

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 (42) hide show
  1. package/lib/mcp/browser/browserContextFactory.js +12 -12
  2. package/lib/mcp/browser/browserServerBackend.js +24 -12
  3. package/lib/mcp/browser/config.js +37 -7
  4. package/lib/mcp/browser/context.js +13 -61
  5. package/lib/mcp/browser/response.js +183 -251
  6. package/lib/mcp/browser/sessionLog.js +19 -104
  7. package/lib/mcp/browser/tab.js +49 -28
  8. package/lib/mcp/browser/tools/common.js +8 -9
  9. package/lib/mcp/browser/tools/console.js +20 -3
  10. package/lib/mcp/browser/tools/dialogs.js +2 -3
  11. package/lib/mcp/browser/tools/evaluate.js +8 -6
  12. package/lib/mcp/browser/tools/files.js +2 -2
  13. package/lib/mcp/browser/tools/form.js +2 -2
  14. package/lib/mcp/browser/tools/install.js +4 -1
  15. package/lib/mcp/browser/tools/keyboard.js +77 -10
  16. package/lib/mcp/browser/tools/mouse.js +59 -7
  17. package/lib/mcp/browser/tools/navigate.js +51 -8
  18. package/lib/mcp/browser/tools/network.js +21 -3
  19. package/lib/mcp/browser/tools/pdf.js +4 -3
  20. package/lib/mcp/browser/tools/runCode.js +8 -12
  21. package/lib/mcp/browser/tools/schema.js +31 -0
  22. package/lib/mcp/browser/tools/screenshot.js +10 -28
  23. package/lib/mcp/browser/tools/snapshot.js +40 -24
  24. package/lib/mcp/browser/tools/tabs.js +10 -10
  25. package/lib/mcp/browser/tools/tool.js +3 -6
  26. package/lib/mcp/browser/tools/tracing.js +3 -3
  27. package/lib/mcp/browser/tools/utils.js +2 -2
  28. package/lib/mcp/browser/tools/verify.js +9 -9
  29. package/lib/mcp/browser/tools/wait.js +3 -3
  30. package/lib/mcp/browser/tools.js +2 -2
  31. package/lib/mcp/extension/extensionContextFactory.js +2 -2
  32. package/lib/mcp/program.js +3 -2
  33. package/lib/mcp/terminal/cli.js +4 -216
  34. package/lib/mcp/terminal/command.js +56 -0
  35. package/lib/mcp/terminal/commands.js +528 -0
  36. package/lib/mcp/terminal/daemon.js +42 -25
  37. package/lib/mcp/terminal/helpGenerator.js +152 -0
  38. package/lib/mcp/terminal/program.js +434 -0
  39. package/lib/mcp/terminal/socketConnection.js +2 -4
  40. package/lib/mcpBundleImpl/index.js +44 -44
  41. package/lib/util.js +3 -6
  42. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,290 +17,152 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var response_exports = {};
20
30
  __export(response_exports, {
21
- RenderedResponse: () => RenderedResponse,
22
31
  Response: () => Response,
23
32
  parseResponse: () => parseResponse,
24
- requestDebug: () => requestDebug
33
+ renderTabMarkdown: () => renderTabMarkdown,
34
+ renderTabsMarkdown: () => renderTabsMarkdown,
35
+ requestDebug: () => requestDebug,
36
+ serializeResponse: () => serializeResponse,
37
+ serializeStructuredResponse: () => serializeStructuredResponse
25
38
  });
26
39
  module.exports = __toCommonJS(response_exports);
40
+ var import_fs = __toESM(require("fs"));
41
+ var import_path = __toESM(require("path"));
27
42
  var import_utilsBundle = require("playwright-core/lib/utilsBundle");
28
43
  var import_tab = require("./tab");
44
+ var import_utils = require("./tools/utils");
45
+ var import_screenshot = require("./tools/screenshot");
29
46
  const requestDebug = (0, import_utilsBundle.debug)("pw:mcp:request");
30
47
  class Response {
31
- constructor(context, toolName, toolArgs) {
32
- this._result = [];
48
+ constructor(ordinal, context, toolName, toolArgs) {
49
+ this._results = [];
50
+ this._errors = [];
33
51
  this._code = [];
34
- this._images = [];
35
- this._files = [];
36
52
  this._includeSnapshot = "none";
37
- this._includeTabs = false;
38
- this._includeMetaOnly = false;
39
53
  this._context = context;
40
54
  this.toolName = toolName;
41
55
  this.toolArgs = toolArgs;
42
56
  }
43
- addResult(result) {
44
- this._result.push(result);
57
+ static {
58
+ this._ordinal = 0;
45
59
  }
46
- addError(error) {
47
- this._result.push(error);
48
- this._isError = true;
60
+ static create(context, toolName, toolArgs) {
61
+ return new Response(++Response._ordinal, context, toolName, toolArgs);
62
+ }
63
+ addTextResult(text) {
64
+ this._results.push({ title: "", text });
49
65
  }
50
- isError() {
51
- return this._isError;
66
+ async addResult(title, data, file) {
67
+ this._results.push({
68
+ text: typeof data === "string" ? data : void 0,
69
+ data: typeof data === "string" ? void 0 : data,
70
+ title,
71
+ file
72
+ });
52
73
  }
53
- result() {
54
- return this._result.join("\n");
74
+ addError(error) {
75
+ this._errors.push(error);
55
76
  }
56
77
  addCode(code) {
57
78
  this._code.push(code);
58
79
  }
59
- code() {
60
- return this._code.join("\n");
61
- }
62
- addImage(image) {
63
- this._images.push(image);
64
- }
65
- images() {
66
- return this._images;
67
- }
68
- async addFile(fileName, options) {
69
- const resolvedFile = await this._context.outputFile(fileName, options);
70
- this._files.push({ fileName: resolvedFile, title: options.reason });
71
- return resolvedFile;
72
- }
73
80
  setIncludeSnapshot() {
74
81
  this._includeSnapshot = this._context.config.snapshot.mode;
75
82
  }
76
- setIncludeFullSnapshot() {
83
+ setIncludeFullSnapshot(includeSnapshotFileName) {
77
84
  this._includeSnapshot = "full";
78
- }
79
- setIncludeTabs() {
80
- this._includeTabs = true;
81
- }
82
- setIncludeModalStates(modalStates) {
83
- this._includeModalStates = modalStates;
84
- }
85
- setIncludeMetaOnly() {
86
- this._includeMetaOnly = true;
87
- }
88
- async finish() {
89
- if (this._tabSnapshot)
90
- return;
91
- if (this._includeSnapshot !== "none" && this._context.currentTab())
92
- this._tabSnapshot = await this._context.currentTabOrDie().captureSnapshot();
93
- for (const tab of this._context.tabs())
94
- await tab.updateTitle();
95
- }
96
- tabSnapshot() {
97
- return this._tabSnapshot;
98
- }
99
- logBegin() {
100
- if (requestDebug.enabled)
101
- requestDebug(this.toolName, this.toolArgs);
102
- }
103
- logEnd() {
104
- if (requestDebug.enabled)
105
- requestDebug(this.serialize());
106
- }
107
- render() {
108
- const renderedResponse = new RenderedResponse();
109
- if (this._result.length)
110
- renderedResponse.results.push(...this._result);
111
- if (this._code.length)
112
- renderedResponse.code.push(...this._code);
113
- if (this._includeSnapshot !== "none" || this._includeTabs) {
114
- const tabsMarkdown = renderTabsMarkdown(this._context.tabs(), this._includeTabs);
115
- if (tabsMarkdown.length)
116
- renderedResponse.states.tabs = tabsMarkdown.join("\n");
85
+ this._includeSnapshotFileName = includeSnapshotFileName;
86
+ }
87
+ async build() {
88
+ const rootPath = this._context.firstRootPath();
89
+ const sections = [];
90
+ const addSection = (title) => {
91
+ const section = { title, content: [], isError: title === "Error" };
92
+ sections.push(section);
93
+ return section.content;
94
+ };
95
+ if (this._errors.length) {
96
+ const content = addSection("Error");
97
+ content.push({ text: this._errors.join("\n"), title: "error" });
117
98
  }
118
- if (this._tabSnapshot?.modalStates.length) {
119
- const modalStatesMarkdown = (0, import_tab.renderModalStates)(this._tabSnapshot.modalStates);
120
- renderedResponse.states.modal = modalStatesMarkdown.join("\n");
121
- } else if (this._tabSnapshot) {
122
- renderTabSnapshot(this._tabSnapshot, this._includeSnapshot, renderedResponse);
123
- } else if (this._includeModalStates) {
124
- const modalStatesMarkdown = (0, import_tab.renderModalStates)(this._includeModalStates);
125
- renderedResponse.states.modal = modalStatesMarkdown.join("\n");
99
+ if (this._results.length) {
100
+ const content = addSection("Result");
101
+ content.push(...this._results);
126
102
  }
127
- if (this._files.length) {
128
- const lines = [];
129
- for (const file of this._files)
130
- lines.push(`- [${file.title}](${file.fileName})`);
131
- renderedResponse.updates.push({ category: "files", content: lines.join("\n") });
103
+ if (this._context.config.codegen !== "none" && this._code.length) {
104
+ const content = addSection("Ran Playwright code");
105
+ for (const code of this._code)
106
+ content.push({ text: code, title: "code" });
132
107
  }
133
- return this._context.config.secrets ? renderedResponse.redact(this._context.config.secrets) : renderedResponse;
134
- }
135
- serialize(options = {}) {
136
- const renderedResponse = this.render();
137
- const includeMeta = options._meta && "dev.lowire/history" in options._meta && "dev.lowire/state" in options._meta;
138
- const _meta = includeMeta ? renderedResponse.asMeta() : void 0;
139
- const content = [
140
- {
141
- type: "text",
142
- text: renderedResponse.asText(this._includeMetaOnly ? { categories: ["files"] } : void 0)
108
+ const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot() : void 0;
109
+ const tabHeaders = await Promise.all(this._context.tabs().map((tab) => tab.headerSnapshot()));
110
+ if (this._includeSnapshot !== "none" || tabHeaders.some((header) => header.changed)) {
111
+ if (tabHeaders.length !== 1) {
112
+ const content2 = addSection("Open tabs");
113
+ content2.push({ text: renderTabsMarkdown(tabHeaders).join("\n"), title: "Open tabs" });
143
114
  }
144
- ];
145
- if (this._includeMetaOnly)
146
- return { _meta, content, isError: this._isError };
147
- if (this._context.config.imageResponses !== "omit") {
148
- for (const image of this._images)
149
- content.push({ type: "image", data: image.data.toString("base64"), mimeType: image.contentType });
115
+ const content = addSection("Page");
116
+ content.push({ text: renderTabMarkdown(tabHeaders[0]).join("\n"), title: "Page" });
150
117
  }
151
- return {
152
- _meta,
153
- content,
154
- isError: this._isError
155
- };
156
- }
157
- }
158
- function renderTabSnapshot(tabSnapshot, includeSnapshot, response) {
159
- if (tabSnapshot.consoleMessages.length) {
160
- const lines2 = [];
161
- for (const message of tabSnapshot.consoleMessages)
162
- lines2.push(`- ${trim(message.toString(), 100)}`);
163
- response.updates.push({ category: "console", content: lines2.join("\n") });
164
- }
165
- if (tabSnapshot.downloads.length) {
166
- const lines2 = [];
167
- for (const entry of tabSnapshot.downloads) {
168
- if (entry.finished)
169
- lines2.push(`- Downloaded file ${entry.download.suggestedFilename()} to ${entry.outputFile}`);
170
- else
171
- lines2.push(`- Downloading file ${entry.download.suggestedFilename()} ...`);
118
+ if (tabSnapshot?.modalStates.length) {
119
+ const content = addSection("Modal state");
120
+ content.push({ text: (0, import_tab.renderModalStates)(this._context.config, tabSnapshot.modalStates).join("\n"), title: "Modal state" });
172
121
  }
173
- response.updates.push({ category: "downloads", content: lines2.join("\n") });
174
- }
175
- if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff === "") {
176
- return;
177
- }
178
- const lines = [];
179
- lines.push(`- Page URL: ${tabSnapshot.url}`);
180
- lines.push(`- Page Title: ${tabSnapshot.title}`);
181
- if (includeSnapshot !== "none") {
182
- lines.push(`- Page Snapshot:`);
183
- lines.push("```yaml");
184
- if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff !== void 0)
185
- lines.push(tabSnapshot.ariaSnapshotDiff);
186
- else
187
- lines.push(tabSnapshot.ariaSnapshot);
188
- lines.push("```");
122
+ if (tabSnapshot && this._includeSnapshot !== "none") {
123
+ const content = addSection("Snapshot");
124
+ const snapshot = this._includeSnapshot === "full" ? tabSnapshot.ariaSnapshot : tabSnapshot.ariaSnapshotDiff ?? tabSnapshot.ariaSnapshot;
125
+ content.push({ text: snapshot, title: "snapshot", file: { prefix: "page", ext: "yml", suggestedFilename: this._includeSnapshotFileName } });
126
+ }
127
+ if (tabSnapshot?.events.filter((event) => event.type !== "request").length) {
128
+ const content = addSection("Events");
129
+ const text = [];
130
+ for (const event of tabSnapshot.events) {
131
+ if (event.type === "console") {
132
+ if ((0, import_tab.shouldIncludeMessage)(this._context.config.console.level, event.message.type))
133
+ text.push(`- ${trimMiddle(event.message.toString(), 100)}`);
134
+ } else if (event.type === "download-start") {
135
+ text.push(`- Downloading file ${event.download.download.suggestedFilename()} ...`);
136
+ } else if (event.type === "download-finish") {
137
+ text.push(`- Downloaded file ${event.download.download.suggestedFilename()} to "${rootPath ? import_path.default.relative(rootPath, event.download.outputFile) : event.download.outputFile}"`);
138
+ }
139
+ }
140
+ content.push({ text: text.join("\n"), title: "events" });
141
+ }
142
+ return sections;
189
143
  }
190
- response.states.page = lines.join("\n");
191
144
  }
192
- function renderTabsMarkdown(tabs, force = false) {
193
- if (tabs.length === 1 && !force)
194
- return [];
145
+ function renderTabMarkdown(tab) {
146
+ const lines = [`- Page URL: ${tab.url}`];
147
+ if (tab.title)
148
+ lines.push(`- Page Title: ${tab.title}`);
149
+ return lines;
150
+ }
151
+ function renderTabsMarkdown(tabs) {
195
152
  if (!tabs.length)
196
- return ['No open tabs. Use the "browser_navigate" tool to navigate to a page first.'];
153
+ return ["No open tabs. Navigate to a URL to create one."];
197
154
  const lines = [];
198
155
  for (let i = 0; i < tabs.length; i++) {
199
156
  const tab = tabs[i];
200
- const current = tab.isCurrentTab() ? " (current)" : "";
201
- lines.push(`- ${i}:${current} [${tab.lastTitle()}] (${tab.page.url()})`);
157
+ const current = tab.current ? " (current)" : "";
158
+ lines.push(`- ${i}:${current} [${tab.title}](${tab.url})`);
202
159
  }
203
160
  return lines;
204
161
  }
205
- function trim(text, maxLength) {
162
+ function trimMiddle(text, maxLength) {
206
163
  if (text.length <= maxLength)
207
164
  return text;
208
- return text.slice(0, maxLength) + "...";
209
- }
210
- class RenderedResponse {
211
- constructor(copy) {
212
- this.states = {};
213
- this.updates = [];
214
- this.results = [];
215
- this.code = [];
216
- if (copy) {
217
- this.states = copy.states;
218
- this.updates = copy.updates;
219
- this.results = copy.results;
220
- this.code = copy.code;
221
- }
222
- }
223
- asText(filter) {
224
- const text = [];
225
- if (this.results.length)
226
- text.push(`### Result
227
- ${this.results.join("\n")}
228
- `);
229
- if (this.code.length)
230
- text.push(`### Ran Playwright code
231
- ${this.code.join("\n")}
232
- `);
233
- for (const { category, content } of this.updates) {
234
- if (filter && !filter.categories.includes(category))
235
- continue;
236
- if (!content.trim())
237
- continue;
238
- switch (category) {
239
- case "console":
240
- text.push(`### New console messages
241
- ${content}
242
- `);
243
- break;
244
- case "downloads":
245
- text.push(`### Downloads
246
- ${content}
247
- `);
248
- break;
249
- case "files":
250
- text.push(`### Files
251
- ${content}
252
- `);
253
- break;
254
- }
255
- }
256
- for (const [category, value] of Object.entries(this.states)) {
257
- if (filter && !filter.categories.includes(category))
258
- continue;
259
- if (!value.trim())
260
- continue;
261
- switch (category) {
262
- case "page":
263
- text.push(`### Page state
264
- ${value}
265
- `);
266
- break;
267
- case "tabs":
268
- text.push(`### Open tabs
269
- ${value}
270
- `);
271
- break;
272
- case "modal":
273
- text.push(`### Modal state
274
- ${value}
275
- `);
276
- break;
277
- }
278
- }
279
- return text.join("\n");
280
- }
281
- asMeta() {
282
- const codeUpdate = this.code.length ? { category: "code", content: this.code.join("\n") } : void 0;
283
- const resultUpdate = this.results.length ? { category: "result", content: this.results.join("\n") } : void 0;
284
- const updates = [resultUpdate, codeUpdate, ...this.updates].filter(Boolean);
285
- return {
286
- "dev.lowire/history": updates,
287
- "dev.lowire/state": { ...this.states }
288
- };
289
- }
290
- redact(secrets) {
291
- const redactText = (text) => {
292
- for (const [secretName, secretValue] of Object.entries(secrets))
293
- text = text.replaceAll(secretValue, `<secret>${secretName}</secret>`);
294
- return text;
295
- };
296
- const updates = this.updates.map((update) => ({ ...update, content: redactText(update.content) }));
297
- const results = this.results.map((result) => redactText(result));
298
- const code = this.code.map((code2) => redactText(code2));
299
- const states = Object.fromEntries(Object.entries(this.states).map(([key, value]) => [key, redactText(value)]));
300
- return new RenderedResponse({ states, updates, results, code });
301
- }
165
+ return text.slice(0, Math.floor(maxLength / 2)) + "..." + text.slice(-3 - Math.floor(maxLength / 2));
302
166
  }
303
167
  function parseSections(text) {
304
168
  const sections = /* @__PURE__ */ new Map();
@@ -313,40 +177,108 @@ function parseSections(text) {
313
177
  }
314
178
  return sections;
315
179
  }
180
+ async function serializeResponse(context, sections, rootPath) {
181
+ const redactText = (text2) => {
182
+ for (const [secretName, secretValue] of Object.entries(context.config.secrets ?? {}))
183
+ text2 = text2.replaceAll(secretValue, `<secret>${secretName}</secret>`);
184
+ return text2;
185
+ };
186
+ const text = [];
187
+ for (const section of sections) {
188
+ text.push(`### ${section.title}`);
189
+ for (const result of section.content) {
190
+ if (!result.file) {
191
+ if (result.text !== void 0)
192
+ text.push(result.text);
193
+ continue;
194
+ }
195
+ if (result.file.suggestedFilename || context.config.outputMode === "file" || result.data) {
196
+ const generatedFileName = await context.outputFile((0, import_utils.dateAsFileName)(result.file.prefix, result.file.ext), { origin: "code", title: section.title });
197
+ const fileName = result.file.suggestedFilename ? await context.outputFile(result.file.suggestedFilename, { origin: "llm", title: section.title }) : generatedFileName;
198
+ text.push(`- [${result.title}](${rootPath ? import_path.default.relative(rootPath, fileName) : fileName})`);
199
+ if (result.data)
200
+ await import_fs.default.promises.writeFile(fileName, result.data, "utf-8");
201
+ else
202
+ await import_fs.default.promises.writeFile(fileName, result.text);
203
+ } else {
204
+ if (result.file.ext === "yml")
205
+ text.push(`\`\`\`yaml
206
+ ${result.text}
207
+ \`\`\``);
208
+ else
209
+ text.push(result.text);
210
+ }
211
+ }
212
+ }
213
+ const content = [
214
+ {
215
+ type: "text",
216
+ text: redactText(text.join("\n"))
217
+ }
218
+ ];
219
+ if (context.config.imageResponses !== "omit") {
220
+ for (const result of sections.flatMap((section) => section.content).filter((result2) => result2.file?.contentType)) {
221
+ const scaledData = (0, import_screenshot.scaleImageToFitMessage)(result.data, result.file.contentType === "image/png" ? "png" : "jpeg");
222
+ content.push({ type: "image", data: scaledData.toString("base64"), mimeType: result.file.contentType });
223
+ }
224
+ }
225
+ return {
226
+ content,
227
+ ...sections.some((section) => section.isError) ? { isError: true } : {}
228
+ };
229
+ }
230
+ async function serializeStructuredResponse(sections) {
231
+ for (const section of sections) {
232
+ for (const result of section.content) {
233
+ if (!result.data)
234
+ continue;
235
+ result.isBase64 = true;
236
+ result.text = result.data.toString("base64");
237
+ result.data = void 0;
238
+ }
239
+ }
240
+ return {
241
+ content: [{ type: "text", text: "", _meta: { sections } }],
242
+ isError: sections.some((section) => section.isError)
243
+ };
244
+ }
316
245
  function parseResponse(response) {
317
246
  if (response.content?.[0].type !== "text")
318
247
  return void 0;
319
248
  const text = response.content[0].text;
320
249
  const sections = parseSections(text);
250
+ const error = sections.get("Error");
321
251
  const result = sections.get("Result");
322
252
  const code = sections.get("Ran Playwright code");
323
253
  const tabs = sections.get("Open tabs");
324
- const pageState = sections.get("Page state");
325
- const consoleMessages = sections.get("New console messages");
254
+ const page = sections.get("Page");
255
+ const snapshot = sections.get("Snapshot");
256
+ const events = sections.get("Events");
326
257
  const modalState = sections.get("Modal state");
327
- const downloads = sections.get("Downloads");
328
- const files = sections.get("Files");
329
258
  const codeNoFrame = code?.replace(/^```js\n/, "").replace(/\n```$/, "");
330
259
  const isError = response.isError;
331
- const attachments = response.content.slice(1);
260
+ const attachments = response.content.length > 1 ? response.content.slice(1) : void 0;
332
261
  return {
333
262
  result,
263
+ error,
334
264
  code: codeNoFrame,
335
265
  tabs,
336
- pageState,
337
- consoleMessages,
266
+ page,
267
+ snapshot,
268
+ events,
338
269
  modalState,
339
- downloads,
340
- files,
341
270
  isError,
342
271
  attachments,
343
- _meta: response._meta
272
+ text
344
273
  };
345
274
  }
346
275
  // Annotate the CommonJS export names for ESM import in node:
347
276
  0 && (module.exports = {
348
- RenderedResponse,
349
277
  Response,
350
278
  parseResponse,
351
- requestDebug
279
+ renderTabMarkdown,
280
+ renderTabsMarkdown,
281
+ requestDebug,
282
+ serializeResponse,
283
+ serializeStructuredResponse
352
284
  });
@@ -33,124 +33,39 @@ __export(sessionLog_exports, {
33
33
  module.exports = __toCommonJS(sessionLog_exports);
34
34
  var import_fs = __toESM(require("fs"));
35
35
  var import_path = __toESM(require("path"));
36
- var import_log = require("../log");
37
36
  var import_config = require("./config");
37
+ var import_response = require("./response");
38
38
  class SessionLog {
39
39
  constructor(sessionFolder) {
40
- this._ordinal = 0;
41
- this._pendingEntries = [];
42
40
  this._sessionFileQueue = Promise.resolve();
43
41
  this._folder = sessionFolder;
44
42
  this._file = import_path.default.join(this._folder, "session.md");
45
43
  }
46
44
  static async create(config, clientInfo) {
47
- const sessionFolder = await (0, import_config.outputFile)(config, clientInfo, `session-${Date.now()}`, { origin: "code", reason: "Saving session" });
45
+ const sessionFolder = await (0, import_config.outputFile)(config, clientInfo, `session-${Date.now()}`, { origin: "code", title: "Saving session" });
48
46
  await import_fs.default.promises.mkdir(sessionFolder, { recursive: true });
49
47
  console.error(`Session: ${sessionFolder}`);
50
48
  return new SessionLog(sessionFolder);
51
49
  }
52
- logResponse(response) {
53
- const entry = {
54
- timestamp: performance.now(),
55
- toolCall: {
56
- toolName: response.toolName,
57
- toolArgs: response.toolArgs,
58
- result: response.result(),
59
- isError: response.isError()
60
- },
61
- code: response.code(),
62
- tabSnapshot: response.tabSnapshot()
63
- };
64
- this._appendEntry(entry);
65
- }
66
- logUserAction(action, tab, code, isUpdate) {
67
- code = code.trim();
68
- if (isUpdate) {
69
- const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
70
- if (lastEntry?.userAction?.name === action.name) {
71
- lastEntry.userAction = action;
72
- lastEntry.code = code;
73
- return;
74
- }
75
- }
76
- if (action.name === "navigate") {
77
- const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
78
- if (lastEntry?.tabSnapshot?.url === action.url)
79
- return;
80
- }
81
- const entry = {
82
- timestamp: performance.now(),
83
- userAction: action,
84
- code,
85
- tabSnapshot: {
86
- url: tab.page.url(),
87
- title: "",
88
- ariaSnapshot: action.ariaSnapshot || "",
89
- modalStates: [],
90
- consoleMessages: [],
91
- downloads: []
92
- }
93
- };
94
- this._appendEntry(entry);
95
- }
96
- _appendEntry(entry) {
97
- this._pendingEntries.push(entry);
98
- if (this._flushEntriesTimeout)
99
- clearTimeout(this._flushEntriesTimeout);
100
- this._flushEntriesTimeout = setTimeout(() => this._flushEntries(), 1e3);
101
- }
102
- async _flushEntries() {
103
- clearTimeout(this._flushEntriesTimeout);
104
- const entries = this._pendingEntries;
105
- this._pendingEntries = [];
50
+ logResponse(toolName, toolArgs, responseObject) {
51
+ const parsed = (0, import_response.parseResponse)(responseObject);
52
+ if (parsed)
53
+ delete parsed.text;
106
54
  const lines = [""];
107
- for (const entry of entries) {
108
- const ordinal = (++this._ordinal).toString().padStart(3, "0");
109
- if (entry.toolCall) {
110
- lines.push(
111
- `### Tool call: ${entry.toolCall.toolName}`,
112
- `- Args`,
113
- "```json",
114
- JSON.stringify(entry.toolCall.toolArgs, null, 2),
115
- "```"
116
- );
117
- if (entry.toolCall.result) {
118
- lines.push(
119
- entry.toolCall.isError ? `- Error` : `- Result`,
120
- "```",
121
- entry.toolCall.result,
122
- "```"
123
- );
124
- }
125
- }
126
- if (entry.userAction) {
127
- const actionData = { ...entry.userAction };
128
- delete actionData.ariaSnapshot;
129
- delete actionData.selector;
130
- delete actionData.signals;
131
- lines.push(
132
- `### User action: ${entry.userAction.name}`,
133
- `- Args`,
134
- "```json",
135
- JSON.stringify(actionData, null, 2),
136
- "```"
137
- );
138
- }
139
- if (entry.code) {
140
- lines.push(
141
- `- Code`,
142
- "```js",
143
- entry.code,
144
- "```"
145
- );
146
- }
147
- if (entry.tabSnapshot) {
148
- const fileName = `${ordinal}.snapshot.yml`;
149
- import_fs.default.promises.writeFile(import_path.default.join(this._folder, fileName), entry.tabSnapshot.ariaSnapshot).catch(import_log.logUnhandledError);
150
- lines.push(`- Snapshot: ${fileName}`);
151
- }
152
- lines.push("", "");
55
+ lines.push(
56
+ `### Tool call: ${toolName}`,
57
+ `- Args`,
58
+ "```json",
59
+ JSON.stringify(toolArgs, null, 2),
60
+ "```"
61
+ );
62
+ if (parsed) {
63
+ lines.push(`- Result`);
64
+ lines.push("```json");
65
+ lines.push(JSON.stringify(parsed, null, 2));
66
+ lines.push("```");
153
67
  }
68
+ lines.push("");
154
69
  this._sessionFileQueue = this._sessionFileQueue.then(() => import_fs.default.promises.appendFile(this._file, lines.join("\n")));
155
70
  }
156
71
  }