thebird 1.2.57 → 1.2.59

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/docs/index.html CHANGED
@@ -31,8 +31,12 @@ bird-chat { display: flex; flex-direction: column; height: 100%; }
31
31
  <div id="pane-term" class="flex flex-col flex-1 overflow-hidden hidden bg-black">
32
32
  <div id="term-container" class="flex-1"></div>
33
33
  </div>
34
- <div id="pane-preview" class="flex-1 overflow-hidden hidden">
35
- <iframe id="preview-frame" class="w-full h-full border-0" src="about:blank"></iframe>
34
+ <div id="pane-preview" class="flex flex-col flex-1 overflow-hidden hidden">
35
+ <div class="flex gap-2 px-3 py-1 bg-base-200 border-b border-base-300 shrink-0">
36
+ <button class="btn btn-xs btn-ghost" onclick="document.getElementById('preview-frame').src='preview/'">↺ Reload</button>
37
+ <span class="text-xs text-base-content/50 self-center">preview/</span>
38
+ </div>
39
+ <iframe id="preview-frame" class="w-full flex-1 border-0" src="about:blank"></iframe>
36
40
  </div>
37
41
  </div>
38
42
  <script>
@@ -41,6 +45,10 @@ function switchTab(t) {
41
45
  document.getElementById('pane-' + id).classList.toggle('hidden', id !== t);
42
46
  document.getElementById('tab-' + id).classList.toggle('tab-active', id === t);
43
47
  });
48
+ if (t === 'preview') {
49
+ const f = document.getElementById('preview-frame');
50
+ if (f.src === 'about:blank' || f.src === '') f.src = 'preview/';
51
+ }
44
52
  }
45
53
  </script>
46
54
  <script type="module" src="app.js"></script>
@@ -40,18 +40,23 @@ self.addEventListener('fetch', e => {
40
40
  e.respondWith(handlePreview(key, e.request));
41
41
  });
42
42
 
43
+ const CORS_HEADERS = {
44
+ 'Cross-Origin-Resource-Policy': 'cross-origin',
45
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
46
+ };
47
+
43
48
  async function handlePreview(key, request) {
44
49
  const db = await openIDB();
45
50
  const fs = await getFS(db);
46
- if (key in fs) return new Response(fs[key], { status: 200, headers: { 'Content-Type': getMime(key) } });
51
+ if (key in fs) return new Response(fs[key], { status: 200, headers: { ...CORS_HEADERS, 'Content-Type': getMime(key) } });
47
52
  const clients = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
48
- if (!clients.length) return new Response('not found: ' + key, { status: 404 });
53
+ if (!clients.length) return new Response('not found: ' + key, { status: 404, headers: CORS_HEADERS });
49
54
  const { port1, port2 } = new MessageChannel();
50
55
  const result = await new Promise((res, rej) => {
51
56
  const t = setTimeout(() => rej(new Error('express timeout')), 5000);
52
57
  port1.onmessage = e => { clearTimeout(t); res(e.data); };
53
58
  clients[0].postMessage({ type: 'EXPRESS_REQUEST', path: '/' + key, method: request.method }, [port2]);
54
59
  });
55
- if (!result || result.status === 404) return new Response('not found: ' + key, { status: 404 });
56
- return new Response(result.body, { status: result.status || 200, headers: { 'Content-Type': result.contentType || 'text/html' } });
60
+ if (!result || result.status === 404) return new Response('not found: ' + key, { status: 404, headers: CORS_HEADERS });
61
+ return new Response(result.body, { status: result.status || 200, headers: { ...CORS_HEADERS, 'Content-Type': result.contentType || 'text/html' } });
57
62
  }
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.57",
3
+ "version": "1.2.59",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
+ "scripts": {
6
+ "start": "node serve.js"
7
+ },
5
8
  "main": "index.js",
6
9
  "types": "index.d.ts",
7
10
  "exports": {
package/serve.js ADDED
@@ -0,0 +1,44 @@
1
+ const http = require('http');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const PORT = process.env.PORT || 8080;
6
+ const ROOT = path.join(__dirname, 'docs');
7
+
8
+ const MIME = {
9
+ '.html': 'text/html',
10
+ '.js': 'application/javascript',
11
+ '.mjs': 'application/javascript',
12
+ '.css': 'text/css',
13
+ '.json': 'application/json',
14
+ '.wasm': 'application/wasm',
15
+ '.png': 'image/png',
16
+ '.svg': 'image/svg+xml',
17
+ '.ico': 'image/x-icon',
18
+ '.txt': 'text/plain',
19
+ };
20
+
21
+ const HEADERS = {
22
+ 'Cross-Origin-Opener-Policy': 'same-origin',
23
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
24
+ 'Cross-Origin-Resource-Policy': 'cross-origin',
25
+ };
26
+
27
+ const server = http.createServer((req, res) => {
28
+ let urlPath = req.url.split('?')[0];
29
+ if (urlPath === '/' || urlPath === '') urlPath = '/index.html';
30
+ const filePath = path.join(ROOT, urlPath);
31
+ if (!filePath.startsWith(ROOT)) { res.writeHead(403); res.end('Forbidden'); return; }
32
+ fs.readFile(filePath, (err, data) => {
33
+ if (err) {
34
+ if (err.code === 'ENOENT') { res.writeHead(404, HEADERS); res.end('Not found: ' + urlPath); }
35
+ else { res.writeHead(500, HEADERS); res.end(err.message); }
36
+ return;
37
+ }
38
+ const ext = path.extname(filePath);
39
+ res.writeHead(200, { ...HEADERS, 'Content-Type': MIME[ext] || 'application/octet-stream' });
40
+ res.end(data);
41
+ });
42
+ });
43
+
44
+ server.listen(PORT, () => console.log('http://localhost:' + PORT));