Send an arbitrary command; returns raw response text. Throws on non-2xx.
(cmd: string, args: string[] = [])
| 147 | |
| 148 | /** Send an arbitrary command; returns raw response text. Throws on non-2xx. */ |
| 149 | async command(cmd: string, args: string[] = []): Promise<string> { |
| 150 | const body = JSON.stringify({ |
| 151 | command: cmd, |
| 152 | args, |
| 153 | ...(this.tabId !== undefined && !isNaN(this.tabId) ? { tabId: this.tabId } : {}), |
| 154 | }); |
| 155 | |
| 156 | let resp: Response; |
| 157 | try { |
| 158 | resp = await fetch(`http://127.0.0.1:${this.port}/command`, { |
| 159 | method: 'POST', |
| 160 | headers: { |
| 161 | 'Content-Type': 'application/json', |
| 162 | 'Authorization': `Bearer ${this.token}`, |
| 163 | }, |
| 164 | body, |
| 165 | signal: AbortSignal.timeout(this.timeoutMs), |
| 166 | }); |
| 167 | } catch (err: any) { |
| 168 | if (err.name === 'TimeoutError' || err.name === 'AbortError') { |
| 169 | throw new BrowseClientError(`browse-client: command "${cmd}" timed out after ${this.timeoutMs}ms`); |
| 170 | } |
| 171 | if (err.code === 'ECONNREFUSED') { |
| 172 | throw new BrowseClientError(`browse-client: daemon not running on port ${this.port}`); |
| 173 | } |
| 174 | throw new BrowseClientError(`browse-client: ${err.message ?? err}`); |
| 175 | } |
| 176 | |
| 177 | const text = await resp.text(); |
| 178 | if (!resp.ok) { |
| 179 | let message = `browse-client: command "${cmd}" failed with status ${resp.status}`; |
| 180 | try { |
| 181 | const parsed = JSON.parse(text); |
| 182 | if (parsed.error) message += `: ${parsed.error}`; |
| 183 | } catch { |
| 184 | if (text) message += `: ${text.slice(0, 200)}`; |
| 185 | } |
| 186 | throw new BrowseClientError(message, resp.status, text); |
| 187 | } |
| 188 | return text; |
| 189 | } |
| 190 | |
| 191 | // ─── Navigation ───────────────────────────────────────────────── |
| 192 |