The network
A plugin reaches the internet only through this.app.net, and only the hosts its manifest names (network:api.example.com) that the writer allowed. Requests go over https; plain http is allowed only to this computer (localhost, 127.0.0.1), for trying a plugin against a server of your own. Redirects aren't followed.
app.net.fetch(url, init?) gets the whole answer, up to 20 MB, within 30 seconds:
const res = await this.app.net.fetch('https://api.example.com/words', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
})
if (res.ok) console.log(res.json()) // also res.text(), res.bytes(), res.status, res.headersapp.net.stream(url, init, onChunk) passes the answer on as it comes, each piece to onChunk as text: an AI's reply, word by word, say. It resolves, when the answer is done, with its status and headers. It runs up to ten minutes, and 200 MB.
const status = this.addStatusBarItem({ id: 'reply', text: '' })
let reply = ''
const res = await this.app.net.stream(
'https://api.example.com/v1/chat',
{ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, stream: true }) },
(text) => {
reply += text
status.setText(reply.slice(-40))
},
)
if (!res.ok) this.app.ui.notify(`The service answered ${res.status}`)Each piece is text as it arrived, not a whole line or message. An event stream's data: lines can be split across pieces, so gather them before reading them.
There's one catch with a text/plain answer: without the header X-Content-Type-Options: nosniff it may arrive whole, up to its first 512 bytes, because Chromium reads that much to guess what kind of answer it is. Event streams (text/event-stream, what AI services send) and JSON arrive as they're sent.
Both work the same in a sandboxed plugin. A plugin can't open a WebSocket.