dev3000 0.0.112 → 0.0.113

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 (102) hide show
  1. package/dist/cdp-monitor.d.ts +2 -1
  2. package/dist/cdp-monitor.d.ts.map +1 -1
  3. package/dist/cdp-monitor.js +23 -6
  4. package/dist/cdp-monitor.js.map +1 -1
  5. package/dist/cli.js +1 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/dev-environment.d.ts +1 -0
  8. package/dist/dev-environment.d.ts.map +1 -1
  9. package/dist/dev-environment.js +2 -1
  10. package/dist/dev-environment.js.map +1 -1
  11. package/mcp-server/.next/BUILD_ID +1 -1
  12. package/mcp-server/.next/build-manifest.json +2 -2
  13. package/mcp-server/.next/fallback-build-manifest.json +2 -2
  14. package/mcp-server/.next/prerender-manifest.json +3 -3
  15. package/mcp-server/.next/server/app/_global-error/page.js.nft.json +1 -1
  16. package/mcp-server/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  17. package/mcp-server/.next/server/app/_global-error.html +2 -2
  18. package/mcp-server/.next/server/app/_global-error.rsc +1 -1
  19. package/mcp-server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  20. package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  21. package/mcp-server/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  22. package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  23. package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  24. package/mcp-server/.next/server/app/_not-found/page.js.nft.json +1 -1
  25. package/mcp-server/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  26. package/mcp-server/.next/server/app/_not-found.html +1 -1
  27. package/mcp-server/.next/server/app/_not-found.rsc +2 -2
  28. package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  29. package/mcp-server/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  30. package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  31. package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  32. package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  33. package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  34. package/mcp-server/.next/server/app/auth/error/page.js.nft.json +1 -1
  35. package/mcp-server/.next/server/app/auth/error/page_client-reference-manifest.js +1 -1
  36. package/mcp-server/.next/server/app/auth/error.html +1 -1
  37. package/mcp-server/.next/server/app/auth/error.rsc +2 -2
  38. package/mcp-server/.next/server/app/auth/error.segments/_full.segment.rsc +2 -2
  39. package/mcp-server/.next/server/app/auth/error.segments/_head.segment.rsc +1 -1
  40. package/mcp-server/.next/server/app/auth/error.segments/_index.segment.rsc +2 -2
  41. package/mcp-server/.next/server/app/auth/error.segments/_tree.segment.rsc +2 -2
  42. package/mcp-server/.next/server/app/auth/error.segments/auth/error/__PAGE__.segment.rsc +1 -1
  43. package/mcp-server/.next/server/app/auth/error.segments/auth/error.segment.rsc +1 -1
  44. package/mcp-server/.next/server/app/auth/error.segments/auth.segment.rsc +1 -1
  45. package/mcp-server/.next/server/app/index.html +1 -1
  46. package/mcp-server/.next/server/app/index.rsc +2 -2
  47. package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  48. package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +2 -2
  49. package/mcp-server/.next/server/app/index.segments/_head.segment.rsc +1 -1
  50. package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +2 -2
  51. package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  52. package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
  53. package/mcp-server/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  54. package/mcp-server/.next/server/app/page.js.nft.json +1 -1
  55. package/mcp-server/.next/server/app/page_client-reference-manifest.js +1 -1
  56. package/mcp-server/.next/server/app/signin/page.js.nft.json +1 -1
  57. package/mcp-server/.next/server/app/signin/page_client-reference-manifest.js +1 -1
  58. package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +1 -1
  59. package/mcp-server/.next/server/app/video/[session]/page_client-reference-manifest.js +1 -1
  60. package/mcp-server/.next/server/app/workflows/[id]/report/page.js.nft.json +1 -1
  61. package/mcp-server/.next/server/app/workflows/[id]/report/page_client-reference-manifest.js +1 -1
  62. package/mcp-server/.next/server/app/workflows/new/page.js.nft.json +1 -1
  63. package/mcp-server/.next/server/app/workflows/new/page_client-reference-manifest.js +1 -1
  64. package/mcp-server/.next/server/app/workflows/page.js.nft.json +1 -1
  65. package/mcp-server/.next/server/app/workflows/page_client-reference-manifest.js +1 -1
  66. package/mcp-server/.next/server/chunks/[root-of-the-server]__157de66b._.js +38 -32
  67. package/mcp-server/.next/server/chunks/[root-of-the-server]__157de66b._.js.map +1 -1
  68. package/mcp-server/.next/server/chunks/[root-of-the-server]__446f0436._.js.map +1 -1
  69. package/mcp-server/.next/server/chunks/[root-of-the-server]__730a8fd0._.js.map +1 -1
  70. package/mcp-server/.next/server/chunks/[root-of-the-server]__c508da18._.js +1 -1
  71. package/mcp-server/.next/server/chunks/[root-of-the-server]__c508da18._.js.map +1 -1
  72. package/mcp-server/.next/server/chunks/bee4f_next_dist_esm_build_templates_app-route_1ece9366.js +6 -5
  73. package/mcp-server/.next/server/chunks/bee4f_next_dist_esm_build_templates_app-route_1ece9366.js.map +1 -1
  74. package/mcp-server/.next/server/chunks/mcp-server_app_api_cloud_fix-workflow_steps_ts_b65f3271._.js +29 -23
  75. package/mcp-server/.next/server/chunks/mcp-server_app_api_cloud_fix-workflow_steps_ts_b65f3271._.js.map +1 -1
  76. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__0ff05d72._.js +3 -0
  77. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__0ff05d72._.js.map +1 -0
  78. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__9a29b768._.js → [root-of-the-server]__27cc5956._.js} +2 -2
  79. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__9a29b768._.js.map → [root-of-the-server]__27cc5956._.js.map} +1 -1
  80. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__8ce53954._.js → [root-of-the-server]__34731fcf._.js} +2 -2
  81. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__34731fcf._.js.map +1 -0
  82. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__97dbdf3d._.js → [root-of-the-server]__570677dc._.js} +2 -2
  83. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__97dbdf3d._.js.map → [root-of-the-server]__570677dc._.js.map} +1 -1
  84. package/mcp-server/.next/server/chunks/ssr/_cd4dc25e._.js.map +1 -1
  85. package/mcp-server/.next/server/server-reference-manifest.js +1 -1
  86. package/mcp-server/.next/server/server-reference-manifest.json +1 -1
  87. package/mcp-server/.next/static/chunks/{8d5a57e1de949e9f.js → 07848f6bd2a7e5f6.js} +2 -2
  88. package/mcp-server/.next/static/chunks/aed4fb5252a4bc95.js +3 -0
  89. package/mcp-server/.next/static/chunks/e8d521464b0c96ca.css +1 -0
  90. package/mcp-server/app/api/cloud/fix-workflow/steps.ts +236 -63
  91. package/mcp-server/app/api/cloud/fix-workflow/workflow.ts +19 -6
  92. package/mcp-server/app/api/cloud/start-fix/route.ts +2 -1
  93. package/mcp-server/app/workflows/workflows-client.tsx +35 -0
  94. package/package.json +1 -1
  95. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__3a2dacc6._.js +0 -3
  96. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__3a2dacc6._.js.map +0 -1
  97. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__8ce53954._.js.map +0 -1
  98. package/mcp-server/.next/static/chunks/2bcc973e2858aaba.js +0 -1
  99. package/mcp-server/.next/static/chunks/a0e4d9b8d2f627cf.css +0 -1
  100. /package/mcp-server/.next/static/{aFliqsQDgiIm8sdHtWiID → UcmWUkU-l9iLeWRnSUybj}/_buildManifest.js +0 -0
  101. /package/mcp-server/.next/static/{aFliqsQDgiIm8sdHtWiID → UcmWUkU-l9iLeWRnSUybj}/_clientMiddlewareManifest.json +0 -0
  102. /package/mcp-server/.next/static/{aFliqsQDgiIm8sdHtWiID → UcmWUkU-l9iLeWRnSUybj}/_ssgManifest.js +0 -0
@@ -55,36 +55,56 @@ export async function createD3kSandbox(
55
55
  let clsData: unknown = null
56
56
  let mcpError: string | null = null
57
57
 
58
+ // Helper function to properly consume sandbox command output
59
+ // The Vercel Sandbox SDK returns a result object with an async logs() iterator
60
+ async function runSandboxCommand(
61
+ sandbox: typeof sandboxResult.sandbox,
62
+ cmd: string,
63
+ args: string[]
64
+ ): Promise<{ exitCode: number; stdout: string; stderr: string }> {
65
+ const result = await sandbox.runCommand({ cmd, args })
66
+ let stdout = ""
67
+ let stderr = ""
68
+ for await (const log of result.logs()) {
69
+ if (log.stream === "stdout") {
70
+ stdout += log.data
71
+ } else {
72
+ stderr += log.data
73
+ }
74
+ }
75
+ await result.wait()
76
+ return { exitCode: result.exitCode, stdout, stderr }
77
+ }
78
+
58
79
  try {
59
80
  // Call fix_my_app MCP tool via curl from inside the sandbox
60
81
  // This avoids network isolation issues - we're calling localhost:3684 from within the sandbox
61
- const mcpCommand = `curl -s -X POST http://localhost:3684/mcp \\
62
- -H "Content-Type: application/json" \\
63
- -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"fix_my_app","arguments":{"mode":"snapshot","focusArea":"performance","returnRawData":true}}}'`
82
+ const mcpCommand = `curl -s -X POST http://localhost:3684/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"fix_my_app","arguments":{"mode":"snapshot","focusArea":"performance","returnRawData":true}}}'`
64
83
 
65
84
  console.log(`[Step 0] Executing MCP command inside sandbox...`)
66
- const mcpResult = await sandboxResult.sandbox.runCommand({
67
- cmd: "bash",
68
- args: ["-c", mcpCommand]
69
- })
85
+ console.log(`[Step 0] MCP command: ${mcpCommand.substring(0, 200)}...`)
70
86
 
71
- console.log(`[Step 0] MCP command exit code: ${mcpResult.exitCode}`)
72
-
73
- // Handle stdout which can be a string or a function
74
- // The Vercel Sandbox SDK types stdout as a union of string | function
75
- let stdout: string
76
- const rawStdout = mcpResult.stdout
77
- if (typeof rawStdout === "string") {
78
- stdout = rawStdout as string
79
- } else if (typeof rawStdout === "function") {
80
- stdout = await (rawStdout as () => Promise<string>)()
81
- } else {
82
- stdout = String(rawStdout || "")
83
- }
87
+ let stdout = ""
88
+ let stderr = ""
89
+ let exitCode = -1
84
90
 
85
- console.log(`[Step 0] MCP stdout length: ${stdout.length} bytes`)
91
+ try {
92
+ const result = await runSandboxCommand(sandboxResult.sandbox, "bash", ["-c", mcpCommand])
93
+ stdout = result.stdout
94
+ stderr = result.stderr
95
+ exitCode = result.exitCode
96
+ console.log(`[Step 0] MCP command exit code: ${exitCode}`)
97
+ console.log(`[Step 0] MCP stdout length: ${stdout.length} bytes`)
98
+ if (stderr) {
99
+ console.log(`[Step 0] MCP stderr: ${stderr.substring(0, 500)}`)
100
+ }
101
+ } catch (runCommandError) {
102
+ const errorMsg = runCommandError instanceof Error ? runCommandError.message : String(runCommandError)
103
+ console.log(`[Step 0] sandbox.runCommand threw: ${errorMsg}`)
104
+ mcpError = `sandbox.runCommand failed: ${errorMsg}`
105
+ }
86
106
 
87
- if (mcpResult.exitCode === 0 && stdout) {
107
+ if (exitCode === 0 && stdout) {
88
108
  try {
89
109
  const mcpResponse = JSON.parse(stdout)
90
110
  if (mcpResponse.result?.content) {
@@ -111,11 +131,11 @@ export async function createD3kSandbox(
111
131
  console.log(`[Step 0] ${mcpError}`)
112
132
  console.log(`[Step 0] Raw stdout: ${stdout.substring(0, 1000)}`)
113
133
  }
114
- } else {
115
- mcpError = `MCP command failed with exit code ${mcpResult.exitCode}`
134
+ } else if (exitCode !== 0 && !mcpError) {
135
+ mcpError = `MCP command failed with exit code ${exitCode}`
116
136
  console.log(`[Step 0] ${mcpError}`)
117
- if (mcpResult.stderr) {
118
- console.log(`[Step 0] stderr: ${mcpResult.stderr}`)
137
+ if (stderr) {
138
+ console.log(`[Step 0] stderr: ${stderr}`)
119
139
  }
120
140
  }
121
141
  } catch (error) {
@@ -123,6 +143,19 @@ export async function createD3kSandbox(
123
143
  console.log(`[Step 0] ${mcpError}`)
124
144
  }
125
145
 
146
+ // Dump all sandbox logs before returning for debugging
147
+ console.log(`[Step 0] === Dumping sandbox logs before returning ===`)
148
+ try {
149
+ const logsResult = await runSandboxCommand(sandboxResult.sandbox, "sh", [
150
+ "-c",
151
+ 'for log in /home/vercel-sandbox/.d3k/logs/*.log; do [ -f "$log" ] && echo "=== $log ===" && tail -100 "$log" || true; done 2>/dev/null || echo "No log files found"'
152
+ ])
153
+ console.log(logsResult.stdout)
154
+ } catch (logsError) {
155
+ console.log(`[Step 0] Failed to dump logs: ${logsError instanceof Error ? logsError.message : String(logsError)}`)
156
+ }
157
+ console.log(`[Step 0] === End sandbox log dump ===`)
158
+
126
159
  // Note: We cannot return the cleanup function or sandbox object as they're not serializable
127
160
  // Sandbox cleanup will happen automatically when the sandbox times out
128
161
  return {
@@ -150,7 +183,7 @@ export async function fetchRealLogs(
150
183
  // If we already have CLS data from Step 0, use it
151
184
  if (clsData) {
152
185
  console.log("[Step 1] Using CLS data captured in Step 0")
153
- return JSON.stringify(clsData, null, 2)
186
+ return { logAnalysis: JSON.stringify(clsData, null, 2), beforeScreenshotUrl: null }
154
187
  }
155
188
 
156
189
  // If there was an MCP error in Step 0, log it
@@ -181,7 +214,10 @@ export async function fetchRealLogs(
181
214
  console.log("[Step 1] Using d3k MCP server to capture CLS metrics and errors...")
182
215
 
183
216
  // First, validate MCP server access and list available tools
217
+ // Use a 30-second timeout to avoid hanging the entire workflow
184
218
  console.log("[Step 1] Validating d3k MCP server access...")
219
+ const validationController = new AbortController()
220
+ const validationTimeout = setTimeout(() => validationController.abort(), 30000)
185
221
  try {
186
222
  const toolsResponse = await fetch(`${mcpUrl}/mcp`, {
187
223
  method: "POST",
@@ -193,13 +229,32 @@ export async function fetchRealLogs(
193
229
  jsonrpc: "2.0",
194
230
  id: 0,
195
231
  method: "tools/list"
196
- })
232
+ }),
233
+ signal: validationController.signal
197
234
  })
235
+ clearTimeout(validationTimeout)
198
236
 
199
237
  if (toolsResponse.ok) {
200
238
  const toolsText = await toolsResponse.text()
201
239
  try {
202
- const toolsData = JSON.parse(toolsText)
240
+ // Parse SSE response format: "event: message\ndata: {...}\n\n"
241
+ let toolsData = null
242
+ const lines = toolsText.split("\n")
243
+ for (const line of lines) {
244
+ if (line.startsWith("data: ")) {
245
+ try {
246
+ toolsData = JSON.parse(line.substring(6))
247
+ break
248
+ } catch {
249
+ // Continue to next line
250
+ }
251
+ }
252
+ }
253
+ // Fallback: try parsing the whole response as JSON (non-SSE format)
254
+ if (!toolsData) {
255
+ toolsData = JSON.parse(toolsText)
256
+ }
257
+
203
258
  const toolNames = toolsData.result?.tools?.map((t: { name: string }) => t.name) || []
204
259
  console.log(`[Step 1] ✅ d3k MCP server accessible`)
205
260
  console.log(`[Step 1] Available tools (${toolNames.length}): ${toolNames.join(", ")}`)
@@ -219,47 +274,132 @@ export async function fetchRealLogs(
219
274
  console.log(`[Step 1] ⚠️ MCP server not accessible: ${toolsResponse.status}`)
220
275
  }
221
276
  } catch (error) {
222
- console.log(
223
- `[Step 1] ⚠️ Failed to validate MCP server: ${error instanceof Error ? error.message : String(error)}`
224
- )
277
+ clearTimeout(validationTimeout)
278
+ const errorMsg = error instanceof Error ? error.message : String(error)
279
+ const isTimeout = error instanceof Error && error.name === "AbortError"
280
+ console.log(`[Step 1] ⚠️ Failed to validate MCP server: ${isTimeout ? "Timed out after 30s" : errorMsg}`)
225
281
  }
226
282
 
227
- // Navigate to the app to generate logs
283
+ // Navigate to the app to generate logs (with 30s timeout)
228
284
  console.log("[Step 1] Navigating browser to app URL...")
229
- const navResponse = await fetch(`${mcpUrl}/mcp`, {
230
- method: "POST",
231
- headers: {
232
- "Content-Type": "application/json",
233
- Accept: "application/json, text/event-stream"
234
- },
235
- body: JSON.stringify({
236
- jsonrpc: "2.0",
237
- id: 0,
238
- method: "tools/call",
239
- params: {
240
- name: "execute_browser_action",
241
- arguments: {
242
- action: "navigate",
243
- params: { url: urlWithBypass }
285
+ const navController = new AbortController()
286
+ const navTimeout = setTimeout(() => navController.abort(), 30000)
287
+ try {
288
+ const navResponse = await fetch(`${mcpUrl}/mcp`, {
289
+ method: "POST",
290
+ headers: {
291
+ "Content-Type": "application/json",
292
+ Accept: "application/json, text/event-stream"
293
+ },
294
+ body: JSON.stringify({
295
+ jsonrpc: "2.0",
296
+ id: 0,
297
+ method: "tools/call",
298
+ params: {
299
+ name: "execute_browser_action",
300
+ arguments: {
301
+ action: "navigate",
302
+ params: { url: urlWithBypass }
303
+ }
244
304
  }
245
- }
305
+ }),
306
+ signal: navController.signal
246
307
  })
247
- })
308
+ clearTimeout(navTimeout)
248
309
 
249
- if (navResponse.ok) {
250
- console.log("[Step 1] Browser navigation completed")
251
- } else {
252
- console.log(`[Step 1] Browser navigation failed: ${navResponse.status}`)
310
+ if (navResponse.ok) {
311
+ console.log("[Step 1] Browser navigation completed")
312
+ } else {
313
+ console.log(`[Step 1] Browser navigation failed: ${navResponse.status}`)
314
+ }
315
+ } catch (navError) {
316
+ clearTimeout(navTimeout)
317
+ const isTimeout = navError instanceof Error && navError.name === "AbortError"
318
+ console.log(
319
+ `[Step 1] Browser navigation error: ${isTimeout ? "Timed out after 30s" : navError instanceof Error ? navError.message : String(navError)}`
320
+ )
253
321
  }
254
322
 
255
323
  // Wait for page to fully load
256
324
  console.log("[Step 1] Waiting 5s for page load...")
257
325
  await new Promise((resolve) => setTimeout(resolve, 5000))
258
326
 
259
- // Check d3k logs to see if it's capturing data
327
+ // Capture "before" screenshot to prove the page loaded and for later comparison
328
+ let beforeScreenshotUrl: string | null = null
329
+ console.log("[Step 1] Capturing 'before' screenshot...")
330
+ const screenshotController = new AbortController()
331
+ const screenshotTimeout = setTimeout(() => screenshotController.abort(), 30000)
332
+ try {
333
+ const screenshotResponse = await fetch(`${mcpUrl}/mcp`, {
334
+ method: "POST",
335
+ headers: {
336
+ "Content-Type": "application/json",
337
+ Accept: "application/json, text/event-stream"
338
+ },
339
+ body: JSON.stringify({
340
+ jsonrpc: "2.0",
341
+ id: 0,
342
+ method: "tools/call",
343
+ params: {
344
+ name: "chrome-devtools_take_snapshot",
345
+ arguments: {}
346
+ }
347
+ }),
348
+ signal: screenshotController.signal
349
+ })
350
+ clearTimeout(screenshotTimeout)
351
+
352
+ if (screenshotResponse.ok) {
353
+ const screenshotText = await screenshotResponse.text()
354
+ // Parse SSE response to get screenshot data
355
+ const lines = screenshotText.split("\n")
356
+ for (const line of lines) {
357
+ if (line.startsWith("data: ")) {
358
+ try {
359
+ const json = JSON.parse(line.substring(6))
360
+ if (json.result?.content) {
361
+ for (const content of json.result.content) {
362
+ if (content.type === "image" && content.data) {
363
+ // Upload base64 image to Vercel Blob
364
+ const imageBuffer = Buffer.from(content.data, "base64")
365
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
366
+ const filename = `screenshot-before-${timestamp}.png`
367
+ const blob = await put(filename, imageBuffer, {
368
+ access: "public",
369
+ contentType: "image/png"
370
+ })
371
+ beforeScreenshotUrl = blob.url
372
+ console.log(`[Step 1] ✅ Before screenshot uploaded: ${beforeScreenshotUrl}`)
373
+ }
374
+ }
375
+ }
376
+ } catch {
377
+ // Continue parsing other lines
378
+ }
379
+ }
380
+ }
381
+ if (!beforeScreenshotUrl) {
382
+ console.log(`[Step 1] Screenshot response received but no image data found`)
383
+ console.log(`[Step 1] Response preview: ${screenshotText.substring(0, 500)}`)
384
+ }
385
+ } else {
386
+ console.log(`[Step 1] Screenshot request failed: ${screenshotResponse.status}`)
387
+ }
388
+ } catch (error) {
389
+ clearTimeout(screenshotTimeout)
390
+ const isTimeout = error instanceof Error && error.name === "AbortError"
391
+ console.log(
392
+ `[Step 1] Screenshot capture error: ${isTimeout ? "Timed out after 30s" : error instanceof Error ? error.message : String(error)}`
393
+ )
394
+ }
395
+
396
+ // Check d3k logs to see if it's capturing data (with 15s timeout)
260
397
  console.log("[Step 1] Fetching d3k logs from sandbox to verify it's working...")
398
+ const logsController = new AbortController()
399
+ const logsTimeout = setTimeout(() => logsController.abort(), 15000)
261
400
  try {
262
- const logsResponse = await fetch(`${mcpUrl}/api/logs`)
401
+ const logsResponse = await fetch(`${mcpUrl}/api/logs`, { signal: logsController.signal })
402
+ clearTimeout(logsTimeout)
263
403
  if (logsResponse.ok) {
264
404
  const logsText = await logsResponse.text()
265
405
  console.log(`[Step 1] d3k logs (last 1000 chars):\n${logsText.slice(-1000)}`)
@@ -267,7 +407,11 @@ export async function fetchRealLogs(
267
407
  console.log(`[Step 1] Could not fetch d3k logs: ${logsResponse.status}`)
268
408
  }
269
409
  } catch (error) {
270
- console.log(`[Step 1] Failed to fetch d3k logs: ${error instanceof Error ? error.message : String(error)}`)
410
+ clearTimeout(logsTimeout)
411
+ const isTimeout = error instanceof Error && error.name === "AbortError"
412
+ console.log(
413
+ `[Step 1] Failed to fetch d3k logs: ${isTimeout ? "Timed out after 15s" : error instanceof Error ? error.message : String(error)}`
414
+ )
271
415
  }
272
416
 
273
417
  // Call fix_my_app with focusArea='performance' to capture CLS and jank
@@ -353,7 +497,10 @@ export async function fetchRealLogs(
353
497
  console.log(`[Step 1] WARNING: fix_my_app returned NO data. Full response:\n${text}`)
354
498
  }
355
499
 
356
- return `d3k Performance Analysis for ${devUrl}\n\n${logAnalysis}`
500
+ return {
501
+ logAnalysis: `d3k Performance Analysis for ${devUrl}\n\n${logAnalysis}`,
502
+ beforeScreenshotUrl
503
+ }
357
504
  } catch (error) {
358
505
  clearTimeout(timeoutId)
359
506
 
@@ -407,7 +554,7 @@ Format your response as a clear, structured report that helps identify what's br
407
554
  })
408
555
 
409
556
  console.log(`[Step 1] Browser automation response (first 500 chars): ${text.substring(0, 500)}...`)
410
- return `Browser Automation Analysis for ${devUrl}\n\n${text}`
557
+ return { logAnalysis: `Browser Automation Analysis for ${devUrl}\n\n${text}`, beforeScreenshotUrl: null }
411
558
  } catch (error) {
412
559
  console.error("[Step 1] Error with browser automation:", error)
413
560
 
@@ -442,10 +589,13 @@ Format your response as a clear, structured report that helps identify what's br
442
589
  logAnalysis += "No errors detected in response.\n"
443
590
  }
444
591
 
445
- return logAnalysis
592
+ return { logAnalysis, beforeScreenshotUrl: null }
446
593
  } catch (fallbackError) {
447
594
  const errorMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError)
448
- return `Failed to fetch logs from ${devUrl}\n\nError: ${errorMessage}\n\nThis may indicate the dev server is not accessible or has crashed.`
595
+ return {
596
+ logAnalysis: `Failed to fetch logs from ${devUrl}\n\nError: ${errorMessage}\n\nThis may indicate the dev server is not accessible or has crashed.`,
597
+ beforeScreenshotUrl: null
598
+ }
449
599
  }
450
600
  }
451
601
  }
@@ -530,10 +680,32 @@ IMPORTANT:
530
680
  /**
531
681
  * Step 3: Upload fix proposal to blob storage and return URL
532
682
  */
533
- export async function uploadToBlob(fixProposal: string, projectName: string, logAnalysis: string, devUrl: string) {
683
+ export async function uploadToBlob(
684
+ fixProposal: string,
685
+ projectName: string,
686
+ logAnalysis: string,
687
+ devUrl: string,
688
+ beforeScreenshotUrl?: string | null
689
+ ) {
534
690
  "use step"
535
691
 
536
692
  console.log("[Step 3] Uploading fix proposal to blob storage...")
693
+ if (beforeScreenshotUrl) {
694
+ console.log(`[Step 3] Including before screenshot: ${beforeScreenshotUrl}`)
695
+ }
696
+
697
+ // Create screenshot section if we have a screenshot
698
+ const screenshotSection = beforeScreenshotUrl
699
+ ? `## Before Screenshot
700
+
701
+ This screenshot was captured when the sandbox dev server first loaded, proving the page rendered successfully.
702
+
703
+ ![Before Screenshot](${beforeScreenshotUrl})
704
+
705
+ ---
706
+
707
+ `
708
+ : ""
537
709
 
538
710
  // Create enhanced markdown with full context and attribution
539
711
  const timestamp = new Date().toISOString()
@@ -545,7 +717,7 @@ export async function uploadToBlob(fixProposal: string, projectName: string, log
545
717
 
546
718
  ---
547
719
 
548
- ## Original Log Analysis
720
+ ${screenshotSection}## Original Log Analysis
549
721
 
550
722
  \`\`\`
551
723
  ${logAnalysis}
@@ -591,6 +763,7 @@ Learn more at https://github.com/vercel-labs/dev3000
591
763
  projectName,
592
764
  fixProposal,
593
765
  blobUrl: blob.url,
766
+ beforeScreenshotUrl: beforeScreenshotUrl || null,
594
767
  message: "Fix analysis completed and uploaded to blob storage"
595
768
  }
596
769
  }
@@ -73,19 +73,26 @@ export async function cloudFixWorkflow(params: {
73
73
  // If we got CLS data from Step 0, pass it to Step 1 to avoid re-fetching
74
74
  // Use bypass token from sandbox if available, otherwise use provided one
75
75
  const effectiveBypassToken = sandboxInfo?.bypassToken || bypassToken
76
- const logAnalysis = await fetchRealLogs(
76
+ const step1Result = await fetchRealLogs(
77
77
  sandboxInfo?.mcpUrl || devUrl,
78
78
  effectiveBypassToken,
79
79
  sandboxInfo?.devUrl,
80
80
  sandboxInfo?.clsData,
81
81
  sandboxInfo?.mcpError
82
82
  )
83
+ const { logAnalysis, beforeScreenshotUrl } = step1Result
83
84
 
84
85
  // Step 2: Invoke AI agent to analyze logs and create fix
85
86
  const fixProposal = await analyzeLogsWithAgent(logAnalysis, sandboxInfo?.devUrl || devUrl)
86
87
 
87
- // Step 3: Upload to blob storage with full context
88
- const blobResult = await uploadToBlob(fixProposal, projectName, logAnalysis, sandboxInfo?.devUrl || devUrl)
88
+ // Step 3: Upload to blob storage with full context and screenshot
89
+ const blobResult = await uploadToBlob(
90
+ fixProposal,
91
+ projectName,
92
+ logAnalysis,
93
+ sandboxInfo?.devUrl || devUrl,
94
+ beforeScreenshotUrl
95
+ )
89
96
 
90
97
  // Step 4: Create GitHub PR if repo info provided AND there are actual fixes to apply
91
98
  let prResult = null
@@ -124,7 +131,7 @@ async function fetchRealLogs(
124
131
  sandboxDevUrl?: string,
125
132
  clsData?: unknown,
126
133
  mcpError?: string | null
127
- ) {
134
+ ): Promise<{ logAnalysis: string; beforeScreenshotUrl: string | null }> {
128
135
  "use step"
129
136
  const { fetchRealLogs } = await import("./steps")
130
137
  return fetchRealLogs(mcpUrlOrDevUrl, bypassToken, sandboxDevUrl, clsData, mcpError)
@@ -136,10 +143,16 @@ async function analyzeLogsWithAgent(logAnalysis: string, devUrl: string) {
136
143
  return analyzeLogsWithAgent(logAnalysis, devUrl)
137
144
  }
138
145
 
139
- async function uploadToBlob(fixProposal: string, projectName: string, logAnalysis: string, devUrl: string) {
146
+ async function uploadToBlob(
147
+ fixProposal: string,
148
+ projectName: string,
149
+ logAnalysis: string,
150
+ devUrl: string,
151
+ beforeScreenshotUrl?: string | null
152
+ ) {
140
153
  "use step"
141
154
  const { uploadToBlob } = await import("./steps")
142
- return uploadToBlob(fixProposal, projectName, logAnalysis, devUrl)
155
+ return uploadToBlob(fixProposal, projectName, logAnalysis, devUrl, beforeScreenshotUrl)
143
156
  }
144
157
 
145
158
  async function createGitHubPR(
@@ -134,7 +134,8 @@ export async function POST(request: Request) {
134
134
  timestamp: new Date().toISOString(),
135
135
  status: "success",
136
136
  reportBlobUrl: result.blobUrl,
137
- prUrl: result.pr?.prUrl
137
+ prUrl: result.pr?.prUrl,
138
+ beforeScreenshotUrl: result.beforeScreenshotUrl || undefined
138
139
  })
139
140
  console.log(`[Start Fix] Updated workflow run metadata to success: ${runId}`)
140
141
  }
@@ -1,5 +1,6 @@
1
1
  "use client"
2
2
 
3
+ import Image from "next/image"
3
4
  import Link from "next/link"
4
5
  import { useState } from "react"
5
6
  import { Badge } from "@/components/ui/badge"
@@ -68,6 +69,8 @@ export default function WorkflowsClient({ user, initialRuns }: WorkflowsClientPr
68
69
  <TableHead>Project</TableHead>
69
70
  <TableHead>Status</TableHead>
70
71
  <TableHead>Timestamp</TableHead>
72
+ <TableHead>Before</TableHead>
73
+ <TableHead>After</TableHead>
71
74
  <TableHead>Report</TableHead>
72
75
  <TableHead>PR</TableHead>
73
76
  </TableRow>
@@ -96,6 +99,38 @@ export default function WorkflowsClient({ user, initialRuns }: WorkflowsClientPr
96
99
  </Badge>
97
100
  </TableCell>
98
101
  <TableCell className="text-muted-foreground">{new Date(run.timestamp).toLocaleString()}</TableCell>
102
+ <TableCell>
103
+ {run.beforeScreenshotUrl ? (
104
+ <a href={run.beforeScreenshotUrl} target="_blank" rel="noopener noreferrer">
105
+ <Image
106
+ src={run.beforeScreenshotUrl}
107
+ alt="Before"
108
+ width={64}
109
+ height={40}
110
+ className="object-cover rounded border hover:opacity-80 transition-opacity"
111
+ unoptimized
112
+ />
113
+ </a>
114
+ ) : (
115
+ <span className="text-muted-foreground text-xs">-</span>
116
+ )}
117
+ </TableCell>
118
+ <TableCell>
119
+ {run.afterScreenshotUrl ? (
120
+ <a href={run.afterScreenshotUrl} target="_blank" rel="noopener noreferrer">
121
+ <Image
122
+ src={run.afterScreenshotUrl}
123
+ alt="After"
124
+ width={64}
125
+ height={40}
126
+ className="object-cover rounded border hover:opacity-80 transition-opacity"
127
+ unoptimized
128
+ />
129
+ </a>
130
+ ) : (
131
+ <span className="text-muted-foreground text-xs">-</span>
132
+ )}
133
+ </TableCell>
99
134
  <TableCell>
100
135
  {run.reportBlobUrl ? (
101
136
  <Link href={`/workflows/${run.id}/report`} className="text-primary hover:underline">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev3000",
3
- "version": "0.0.112",
3
+ "version": "0.0.113",
4
4
  "description": "AI-powered development tools with browser monitoring and MCP server integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,3 +0,0 @@
1
- module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},65071,(a,b,c)=>{"use strict";b.exports=a.r(18622)},55205,(a,b,c)=>{"use strict";b.exports=a.r(65071).vendored["react-ssr"].ReactJsxRuntime},33857,(a,b,c)=>{"use strict";b.exports=a.r(65071).vendored["react-ssr"].React},27125,(a,b,c)=>{"use strict";b.exports=a.r(65071).vendored.contexts.AppRouterContext},77279,(a,b,c)=>{"use strict";b.exports=a.r(65071).vendored["react-ssr"].ReactServerDOMTurbopackClient},3988,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},71075,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"useMergedRef",{enumerable:!0,get:function(){return e}});let d=a.r(33857);function e(a,b){let c=(0,d.useRef)(null),e=(0,d.useRef)(null);return(0,d.useCallback)(d=>{if(null===d){let a=c.current;a&&(c.current=null,a());let b=e.current;b&&(e.current=null,b())}else a&&(c.current=f(a,d)),b&&(e.current=f(b,d))},[a,b])}function f(a,b){if("function"!=typeof a)return a.current=b,()=>{a.current=null};{let c=a(b);return"function"==typeof c?c:()=>a(null)}}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},32570,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"warnOnce",{enumerable:!0,get:function(){return d}});let d=a=>{}},16274,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={DEFAULT_SEGMENT_KEY:function(){return l},PAGE_SEGMENT_KEY:function(){return k},addSearchParamsIfPageSegment:function(){return i},computeSelectedLayoutSegment:function(){return j},getSegmentValue:function(){return f},getSelectedLayoutSegmentPath:function(){return function a(b,c,d=!0,e=[]){let g;if(d)g=b[1][c];else{let a=b[1];g=a.children??Object.values(a)[0]}if(!g)return e;let h=f(g[0]);return!h||h.startsWith(k)?e:(e.push(h),a(g,c,!1,e))}},isGroupSegment:function(){return g},isParallelRouteSegment:function(){return h}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});function f(a){return Array.isArray(a)?a[1]:a}function g(a){return"("===a[0]&&a.endsWith(")")}function h(a){return a.startsWith("@")&&"@children"!==a}function i(a,b){if(a.includes(k)){let a=JSON.stringify(b);return"{}"!==a?k+"?"+a:k}return a}function j(a,b){if(!a||0===a.length)return null;let c="children"===b?a[0]:a[a.length-1];return c===l?null:c}let k="__PAGE__",l="__DEFAULT__"},60019,a=>{"use strict";var b=a.i(55205),c=a.i(77696),d=a.i(42261),e=a.i(18749);let f=(0,d.cva)("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",{variants:{variant:{default:"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",destructive:"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2 has-[>svg]:px-3",sm:"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",lg:"h-10 rounded-md px-6 has-[>svg]:px-4",icon:"size-9"}},defaultVariants:{variant:"default",size:"default"}});function g({className:a,variant:d,size:g,asChild:h=!1,...i}){let j=h?c.Slot:"button";return(0,b.jsx)(j,{"data-slot":"button",className:(0,e.cn)(f({variant:d,size:g,className:a})),...i})}a.s(["Button",()=>g])},25988,a=>{"use strict";var b=a.i(55205),c=a.i(18749);function d({className:a,...d}){return(0,b.jsx)("div",{"data-slot":"card",className:(0,c.cn)("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",a),...d})}function e({className:a,...d}){return(0,b.jsx)("div",{"data-slot":"card-header",className:(0,c.cn)("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",a),...d})}function f({className:a,...d}){return(0,b.jsx)("div",{"data-slot":"card-title",className:(0,c.cn)("leading-none font-semibold",a),...d})}function g({className:a,...d}){return(0,b.jsx)("div",{"data-slot":"card-content",className:(0,c.cn)("px-6",a),...d})}a.s(["Card",()=>d,"CardContent",()=>g,"CardHeader",()=>e,"CardTitle",()=>f])},24501,a=>{"use strict";var b=a.i(55205),c=a.i(7287),d=a.i(33857),e=a.i(77696),f=a.i(42261),g=a.i(18749);let h=(0,f.cva)("inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",secondary:"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",destructive:"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"}},defaultVariants:{variant:"default"}});function i({className:a,variant:c,asChild:d=!1,...f}){let i=d?e.Slot:"span";return(0,b.jsx)(i,{"data-slot":"badge",className:(0,g.cn)(h({variant:c}),a),...f})}var j=a.i(60019),k=a.i(25988);function l({className:a,...c}){return(0,b.jsx)("div",{"data-slot":"table-container",className:"relative w-full overflow-x-auto",children:(0,b.jsx)("table",{"data-slot":"table",className:(0,g.cn)("w-full caption-bottom text-sm",a),...c})})}function m({className:a,...c}){return(0,b.jsx)("thead",{"data-slot":"table-header",className:(0,g.cn)("[&_tr]:border-b",a),...c})}function n({className:a,...c}){return(0,b.jsx)("tbody",{"data-slot":"table-body",className:(0,g.cn)("[&_tr:last-child]:border-0",a),...c})}function o({className:a,...c}){return(0,b.jsx)("tr",{"data-slot":"table-row",className:(0,g.cn)("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",a),...c})}function p({className:a,...c}){return(0,b.jsx)("th",{"data-slot":"table-head",className:(0,g.cn)("text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",a),...c})}function q({className:a,...c}){return(0,b.jsx)("td",{"data-slot":"table-cell",className:(0,g.cn)("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",a),...c})}function r({user:a,initialRuns:e}){let[f,g]=(0,d.useState)(!1);async function h(){g(!0);try{await fetch("/api/auth/signout",{method:"POST"}),window.location.href="/"}catch(a){console.error("Failed to sign out:",a),g(!1)}}return(0,b.jsx)("div",{className:"min-h-screen bg-gray-50 py-8",children:(0,b.jsxs)("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",children:[(0,b.jsxs)("div",{className:"mb-8 flex justify-between items-start",children:[(0,b.jsxs)("div",{children:[(0,b.jsx)("h1",{className:"text-3xl font-bold text-gray-900",children:"d3k Workflow Runs"}),(0,b.jsx)("p",{className:"mt-2 text-gray-600",children:"View all your d3k workflow fix proposals and PRs"}),(0,b.jsxs)("p",{className:"mt-1 text-sm text-gray-500",children:["Signed in as ",a.email]})]}),(0,b.jsxs)("div",{className:"flex gap-3",children:[(0,b.jsx)(j.Button,{asChild:!0,children:(0,b.jsx)(c.default,{href:"/workflows/new",children:"New Workflow"})}),(0,b.jsx)(j.Button,{variant:"outline",onClick:h,disabled:f,children:f?"Signing out...":"Sign out"})]})]}),0===e.length?(0,b.jsx)(k.Card,{className:"p-12 text-center",children:(0,b.jsxs)(k.CardContent,{children:[(0,b.jsx)("p",{className:"text-muted-foreground",children:"No workflow runs yet"}),(0,b.jsx)("p",{className:"text-sm text-muted-foreground/70 mt-2",children:"Run a workflow from the CLI to see it appear here"})]})}):(0,b.jsx)(k.Card,{children:(0,b.jsxs)(l,{children:[(0,b.jsx)(m,{children:(0,b.jsxs)(o,{children:[(0,b.jsx)(p,{children:"Project"}),(0,b.jsx)(p,{children:"Status"}),(0,b.jsx)(p,{children:"Timestamp"}),(0,b.jsx)(p,{children:"Report"}),(0,b.jsx)(p,{children:"PR"})]})}),(0,b.jsx)(n,{children:e.map(a=>(0,b.jsxs)(o,{children:[(0,b.jsxs)(q,{children:[(0,b.jsx)("div",{className:"font-medium",children:a.projectName}),(0,b.jsx)("div",{className:"text-xs text-muted-foreground",children:a.id})]}),(0,b.jsx)(q,{children:(0,b.jsx)(i,{variant:"success"===a.status?"secondary":"running"===a.status?"default":"destructive",className:"success"===a.status?"bg-green-100 text-green-800 hover:bg-green-100":"running"===a.status?"bg-blue-100 text-blue-800 hover:bg-blue-100":"",children:a.status})}),(0,b.jsx)(q,{className:"text-muted-foreground",children:new Date(a.timestamp).toLocaleString()}),(0,b.jsx)(q,{children:a.reportBlobUrl?(0,b.jsx)(c.default,{href:`/workflows/${a.id}/report`,className:"text-primary hover:underline",children:"View Report"}):(0,b.jsx)("span",{className:"text-muted-foreground",children:"No report"})}),(0,b.jsx)(q,{children:a.prUrl?(0,b.jsx)("a",{href:a.prUrl,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:"View PR"}):(0,b.jsx)("span",{className:"text-muted-foreground",children:"No PR"})})]},`${a.id}-${a.timestamp}`))})]})})]})})}a.s(["default",()=>r],24501)}];
2
-
3
- //# sourceMappingURL=%5Broot-of-the-server%5D__3a2dacc6._.js.map