(message: string, stack: string)
| 201 | } |
| 202 | |
| 203 | function buildIssueURL(message: string, stack: string) { |
| 204 | // Field keys match the ids in .github/ISSUE_TEMPLATE/bug-report.yml so the issue |
| 205 | // form opens pre-filled. Populating os/terminal/reproduce keeps the report past |
| 206 | // the contributing-guidelines compliance check, which pushes for system info. |
| 207 | const url = new URL("https://github.com/anomalyco/opencode/issues/new?template=bug-report.yml") |
| 208 | url.searchParams.set("title", `TUI crash: ${message}`) |
| 209 | url.searchParams.set("opencode-version", InstallationVersion) |
| 210 | url.searchParams.set("os", describeOS()) |
| 211 | url.searchParams.set("terminal", describeTerminal()) |
| 212 | url.searchParams.set( |
| 213 | "reproduce", |
| 214 | "Reported automatically from the opencode crash screen. If you can, describe what you were doing when it crashed.", |
| 215 | ) |
| 216 | |
| 217 | // Budget the stack against the fully URL-encoded length (not the raw length) so |
| 218 | // the final link stays under GitHub's practical limit; flag truncation so a |
| 219 | // clipped trace is obvious. searchParams.set handles encoding without throwing, |
| 220 | // so measuring url.toString() is both correct and safe on any input. |
| 221 | const MAX_URL_LENGTH = 6000 |
| 222 | const marker = "\n... (truncated)" |
| 223 | const head = `The opencode TUI crashed with an unexpected error.\n\n**Error:** ${message}\n\n**Stack trace:**\n` |
| 224 | const setBody = (body: string) => url.searchParams.set("description", head + "```\n" + body + "\n```") |
| 225 | |
| 226 | setBody(stack) |
| 227 | if (url.toString().length <= MAX_URL_LENGTH) return url |
| 228 | |
| 229 | // Largest raw stack prefix whose encoded URL (with the marker) still fits. |
| 230 | let lo = 0 |
| 231 | let hi = stack.length |
| 232 | while (lo < hi) { |
| 233 | const mid = Math.ceil((lo + hi) / 2) |
| 234 | setBody(stack.slice(0, mid) + marker) |
| 235 | if (url.toString().length <= MAX_URL_LENGTH) lo = mid |
| 236 | else hi = mid - 1 |
| 237 | } |
| 238 | setBody(stack.slice(0, lo) + marker) |
| 239 | return url |
| 240 | } |
| 241 | |
| 242 | function describeOS() { |
| 243 | const name = |
no test coverage detected