thebird 1.2.56 → 1.2.58
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 +10 -2
- package/docs/shell-readline.js +130 -0
- package/docs/shell.js +21 -17
- package/package.json +4 -1
- package/serve.js +44 -0
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
|
-
<
|
|
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>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export function createReadline({ term, getCompletions, onLine, getPrompt }) {
|
|
2
|
+
let buf = '';
|
|
3
|
+
let pos = 0;
|
|
4
|
+
let histIdx = -1;
|
|
5
|
+
let escBuf = '';
|
|
6
|
+
let inEsc = false;
|
|
7
|
+
|
|
8
|
+
const write = s => term.write(s);
|
|
9
|
+
|
|
10
|
+
function promptStr() { return '\r\n\x1b[32m' + getPrompt() + ' $ \x1b[0m'; }
|
|
11
|
+
function showPrompt() { write(promptStr()); }
|
|
12
|
+
|
|
13
|
+
function redraw() {
|
|
14
|
+
write('\r\x1b[2K\x1b[32m' + getPrompt() + ' $ \x1b[0m' + buf);
|
|
15
|
+
if (pos < buf.length) write('\x1b[' + (buf.length - pos) + 'D');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function insert(ch) {
|
|
19
|
+
buf = buf.slice(0, pos) + ch + buf.slice(pos);
|
|
20
|
+
pos++;
|
|
21
|
+
redraw();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function delBefore() {
|
|
25
|
+
if (!pos) return;
|
|
26
|
+
buf = buf.slice(0, pos - 1) + buf.slice(pos);
|
|
27
|
+
pos--;
|
|
28
|
+
redraw();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function delAfter() {
|
|
32
|
+
if (pos >= buf.length) return;
|
|
33
|
+
buf = buf.slice(0, pos) + buf.slice(pos + 1);
|
|
34
|
+
redraw();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function moveTo(n) {
|
|
38
|
+
pos = Math.max(0, Math.min(buf.length, n));
|
|
39
|
+
const pLen = getPrompt().length + 3;
|
|
40
|
+
if (pLen + pos > 0) write('\r\x1b[' + (pLen + pos) + 'C');
|
|
41
|
+
else write('\r');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function handleTab() {
|
|
45
|
+
const word = buf.slice(0, pos).split(/\s+/).pop() || '';
|
|
46
|
+
const completions = getCompletions(buf, word);
|
|
47
|
+
if (!completions.length) return;
|
|
48
|
+
if (completions.length === 1) {
|
|
49
|
+
const rest = completions[0].slice(word.length);
|
|
50
|
+
buf = buf.slice(0, pos) + rest + buf.slice(pos);
|
|
51
|
+
pos += rest.length;
|
|
52
|
+
redraw();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const common = completions.reduce((a, b) => {
|
|
56
|
+
let i = 0;
|
|
57
|
+
while (i < a.length && a[i] === b[i]) i++;
|
|
58
|
+
return a.slice(0, i);
|
|
59
|
+
});
|
|
60
|
+
if (common.length > word.length) {
|
|
61
|
+
const rest = common.slice(word.length);
|
|
62
|
+
buf = buf.slice(0, pos) + rest + buf.slice(pos);
|
|
63
|
+
pos += rest.length;
|
|
64
|
+
redraw();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
write('\r\n' + completions.join(' '));
|
|
68
|
+
redraw();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function commit() {
|
|
72
|
+
const line = buf;
|
|
73
|
+
write('\r\n');
|
|
74
|
+
const hist = getHistory();
|
|
75
|
+
if (line.trim()) hist.unshift(line);
|
|
76
|
+
buf = '';
|
|
77
|
+
pos = 0;
|
|
78
|
+
histIdx = -1;
|
|
79
|
+
onLine(line);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getHistory() { return window.__debug?.shell?.history || []; }
|
|
83
|
+
|
|
84
|
+
function histNav(dir) {
|
|
85
|
+
const hist = getHistory();
|
|
86
|
+
if (!hist.length) return;
|
|
87
|
+
histIdx = Math.max(-1, Math.min(hist.length - 1, histIdx + dir));
|
|
88
|
+
buf = histIdx === -1 ? '' : hist[histIdx];
|
|
89
|
+
pos = buf.length;
|
|
90
|
+
redraw();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const ESC_MAP = {
|
|
94
|
+
'[A': () => histNav(1),
|
|
95
|
+
'[B': () => histNav(-1),
|
|
96
|
+
'[C': () => { if (pos < buf.length) { pos++; write('\x1b[C'); } },
|
|
97
|
+
'[D': () => { if (pos > 0) { pos--; write('\x1b[D'); } },
|
|
98
|
+
'[H': () => moveTo(0),
|
|
99
|
+
'[F': () => moveTo(buf.length),
|
|
100
|
+
'[3~': () => delAfter(),
|
|
101
|
+
'[1~': () => moveTo(0),
|
|
102
|
+
'[4~': () => moveTo(buf.length),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
function onData(data) {
|
|
106
|
+
if (inEsc) {
|
|
107
|
+
escBuf += data;
|
|
108
|
+
if (escBuf === '[') return;
|
|
109
|
+
if (escBuf.match(/^\[[\x40-\x7E]$/) || escBuf.match(/^\[\d+~$/)) {
|
|
110
|
+
const handler = ESC_MAP[escBuf];
|
|
111
|
+
if (handler) handler();
|
|
112
|
+
inEsc = false; escBuf = '';
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (escBuf.length > 6) { inEsc = false; escBuf = ''; }
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (data === '\x1b') { inEsc = true; escBuf = ''; return; }
|
|
119
|
+
if (data === '\x01') { moveTo(0); return; }
|
|
120
|
+
if (data === '\x05') { moveTo(buf.length); return; }
|
|
121
|
+
if (data === '\x0b') { buf = buf.slice(0, pos); redraw(); return; }
|
|
122
|
+
if (data === '\x15') { buf = buf.slice(pos); pos = 0; redraw(); return; }
|
|
123
|
+
if (data === '\x09') { handleTab(); return; }
|
|
124
|
+
if (data === '\r') { commit(); return; }
|
|
125
|
+
if (data === '\x7f') { delBefore(); return; }
|
|
126
|
+
if (data >= ' ') { insert(data); return; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { onData, showPrompt };
|
|
130
|
+
}
|
package/docs/shell.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createMachine, createActor } from './vendor/xstate.js';
|
|
2
2
|
import { createNodeEnv } from './shell-node.js';
|
|
3
|
+
import { createReadline } from './shell-readline.js';
|
|
3
4
|
|
|
4
5
|
function resolvePath(cwd, p) {
|
|
5
6
|
if (!p || p === '~') return '/';
|
|
@@ -134,7 +135,6 @@ export function createShell({ term, onPreviewWrite }) {
|
|
|
134
135
|
|
|
135
136
|
async function run(line, onData) {
|
|
136
137
|
if (!line.trim()) return;
|
|
137
|
-
ctx.history.push(line);
|
|
138
138
|
const st = actor.getSnapshot().value;
|
|
139
139
|
if (st === 'node-repl' && line.trim() !== 'exit') { await ctx.nodeEval(line); return; }
|
|
140
140
|
if (line.trim() === 'exit') { BUILTINS.exit(actor); return; }
|
|
@@ -162,16 +162,30 @@ export function createShell({ term, onPreviewWrite }) {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
function getCompletions(line, word) {
|
|
166
|
+
const snap = window.__debug.idbSnapshot || {};
|
|
167
|
+
const files = Object.keys(snap);
|
|
168
|
+
const tokens = line.trim().split(/\s+/);
|
|
169
|
+
if (tokens.length <= 1 && !line.includes(' ')) {
|
|
170
|
+
const cmds = Object.keys(BUILTINS);
|
|
171
|
+
return cmds.filter(c => c.startsWith(word));
|
|
172
|
+
}
|
|
173
|
+
return files.filter(f => f.startsWith(word));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const rl = createReadline({
|
|
177
|
+
term,
|
|
178
|
+
getCompletions,
|
|
179
|
+
getPrompt: () => ctx.cwd,
|
|
180
|
+
onLine: line => run(line, onData).then(() => rl.showPrompt()),
|
|
181
|
+
});
|
|
167
182
|
|
|
168
183
|
function onData(data) {
|
|
169
184
|
if (data === '\x03') {
|
|
170
185
|
actor.send({ type: 'ERROR' });
|
|
171
186
|
inputQueue = [];
|
|
172
|
-
buf = '';
|
|
173
187
|
term.write('^C');
|
|
174
|
-
|
|
188
|
+
rl.showPrompt();
|
|
175
189
|
return;
|
|
176
190
|
}
|
|
177
191
|
const st = actor.getSnapshot().value;
|
|
@@ -179,23 +193,13 @@ export function createShell({ term, onPreviewWrite }) {
|
|
|
179
193
|
inputQueue.push(data);
|
|
180
194
|
return;
|
|
181
195
|
}
|
|
182
|
-
|
|
183
|
-
term.write('\r\n');
|
|
184
|
-
const line = buf;
|
|
185
|
-
buf = '';
|
|
186
|
-
run(line, onData).then(() => prompt());
|
|
187
|
-
} else if (data === '\x7f') {
|
|
188
|
-
if (buf.length) { buf = buf.slice(0, -1); term.write('\x08 \x08'); }
|
|
189
|
-
} else {
|
|
190
|
-
buf += data;
|
|
191
|
-
term.write(data);
|
|
192
|
-
}
|
|
196
|
+
rl.onData(data);
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
term.onData(onData);
|
|
196
200
|
onPreviewWrite && (window.__debug.shell.onPreviewWrite = onPreviewWrite);
|
|
197
201
|
const runPublic = line => run(line, onData);
|
|
198
202
|
window.__debug.shell.run = runPublic;
|
|
199
|
-
|
|
203
|
+
rl.showPrompt();
|
|
200
204
|
return { run: runPublic };
|
|
201
205
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thebird",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.58",
|
|
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));
|