traw 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traw",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "module": "src/index.ts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -202,9 +202,7 @@ What's your next action?`
202
202
  }
203
203
  } catch (err) {
204
204
  const logEntry = `\n--- ${new Date().toISOString()} ---\nError: ${err}\nResponse:\n${response}\n`
205
- import("fs").then(fs => {
206
- fs.appendFileSync("agent-errors.log", logEntry)
207
- }).catch(() => {})
205
+ Bun.write("agent-errors.log", logEntry).catch(() => {})
208
206
  return null
209
207
  }
210
208
  }
@@ -48,13 +48,13 @@ export class MoClient {
48
48
  }),
49
49
  })
50
50
  } catch (err: any) {
51
- clearTimeout(timeoutId)
52
51
  if (err.name === "AbortError") {
53
52
  throw new Error(`mo timeout: no response in ${timeoutMs / 1000}s`)
54
53
  }
55
54
  throw err
55
+ } finally {
56
+ clearTimeout(timeoutId)
56
57
  }
57
- clearTimeout(timeoutId)
58
58
 
59
59
  if (!resp.ok) {
60
60
  const body = await resp.text()
@@ -72,59 +72,53 @@ export class BrowserController {
72
72
 
73
73
  document.querySelectorAll("[data-idx]").forEach(el => el.removeAttribute("data-idx"))
74
74
 
75
+ const skipTags = new Set(["script", "style", "noscript", "svg", "path", "meta", "link", "br", "hr"])
76
+ const interactiveTags = new Set(["a", "button", "input", "textarea", "select"])
77
+ const textTags = new Set(["h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "td", "th", "label"])
78
+
79
+ const getAttrs = (el: HTMLElement, tag: string, idx: number): string[] => {
80
+ const attrs: string[] = [`id="${idx}"`]
81
+ const input = el as HTMLInputElement
82
+ const anchor = el as HTMLAnchorElement
83
+
84
+ if (input.type) attrs.push(`type="${input.type}"`)
85
+ if (tag === "a" && anchor.href) attrs.push(`href="${esc(anchor.href.slice(0, 80))}"`)
86
+ if (input.value) attrs.push(`value="${esc(input.value)}"`)
87
+ if ((el as any).disabled) attrs.push(`disabled="true"`)
88
+ if ((el as any).checked) attrs.push(`checked="true"`)
89
+ if ((el as any).readOnly) attrs.push(`readonly="true"`)
90
+ if ((el as any).required) attrs.push(`required="true"`)
91
+ if (el.getAttribute("aria-expanded")) attrs.push(`expanded="${el.getAttribute("aria-expanded")}"`)
92
+ if (el.getAttribute("aria-selected") === "true") attrs.push(`selected="true"`)
93
+
94
+ return attrs
95
+ }
96
+
75
97
  const walk = (node: Element, depth: number) => {
76
98
  const el = node as HTMLElement
77
99
  const tag = el.tagName.toLowerCase()
78
100
  const indent = " ".repeat(depth)
79
101
 
80
- const skipTags = ["script", "style", "noscript", "svg", "path", "meta", "link", "br", "hr"]
81
- if (skipTags.includes(tag)) return
102
+ if (skipTags.has(tag)) return
82
103
 
83
- const interactiveTags = ["a", "button", "input", "textarea", "select"]
84
- const hasRole = el.getAttribute("role")
85
- const hasOnclick = el.hasAttribute("onclick")
86
- const isInteractive = interactiveTags.includes(tag) || hasRole || hasOnclick
104
+ const isInteractive = interactiveTags.has(tag) || el.getAttribute("role") || el.hasAttribute("onclick")
87
105
 
88
106
  if (isInteractive) {
89
107
  el.setAttribute("data-idx", String(idx))
90
-
91
- const attrs: string[] = [`id="${idx}"`]
92
-
93
- const type = (el as HTMLInputElement).type
94
- if (type) attrs.push(`type="${type}"`)
95
-
96
- const href = (el as HTMLAnchorElement).href
97
- if (href && tag === "a") attrs.push(`href="${esc(href.slice(0, 80))}"`)
98
-
99
- const val = (el as HTMLInputElement).value
100
- if (val) attrs.push(`value="${esc(val)}"`)
101
-
102
- if ((el as any).disabled) attrs.push(`disabled="true"`)
103
- if ((el as any).checked) attrs.push(`checked="true"`)
104
- if ((el as any).readOnly) attrs.push(`readonly="true"`)
105
- if ((el as any).required) attrs.push(`required="true"`)
106
- if (el.getAttribute("aria-expanded")) attrs.push(`expanded="${el.getAttribute("aria-expanded")}"`)
107
- if (el.getAttribute("aria-selected") === "true") attrs.push(`selected="true"`)
108
-
108
+ const attrs = getAttrs(el, tag, idx)
109
109
  const text = el.textContent?.trim() || ""
110
- const ariaLabel = el.getAttribute("aria-label")
111
- const placeholder = (el as HTMLInputElement).placeholder
112
- const label = esc(ariaLabel || text || placeholder || "")
113
-
110
+ const label = esc(el.getAttribute("aria-label") || text || (el as HTMLInputElement).placeholder || "")
114
111
  out.push(`${indent}<${tag} ${attrs.join(" ")}>${label}</${tag}>`)
115
112
  idx++
116
- } else {
117
- const textTags = ["h1", "h2", "h3", "h4", "h5", "h6", "p", "li", "td", "th", "label"]
118
- if (textTags.includes(tag)) {
119
- const directText = Array.from(el.childNodes)
120
- .filter(n => n.nodeType === 3)
121
- .map(n => n.textContent?.trim())
122
- .join(" ")
123
- .trim()
124
-
125
- if (directText.length > 2) {
126
- out.push(`${indent}<${tag}>${esc(directText)}</${tag}>`)
127
- }
113
+ } else if (textTags.has(tag)) {
114
+ const directText = Array.from(el.childNodes)
115
+ .filter(n => n.nodeType === 3)
116
+ .map(n => n.textContent?.trim())
117
+ .join(" ")
118
+ .trim()
119
+
120
+ if (directText.length > 2) {
121
+ out.push(`${indent}<${tag}>${esc(directText)}</${tag}>`)
128
122
  }
129
123
  }
130
124