thebird 1.2.104 → 1.2.106
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/.gm/lastskill +1 -1
- package/docs/index.html +553 -25
- package/package.json +1 -1
package/.gm/lastskill
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
gm:
|
|
1
|
+
gm:gm-execute
|
package/docs/index.html
CHANGED
|
@@ -6,11 +6,333 @@
|
|
|
6
6
|
<meta name="theme-color" content="#247420" media="(prefers-color-scheme: light)">
|
|
7
7
|
<meta name="theme-color" content="#3FA93A" media="(prefers-color-scheme: dark)">
|
|
8
8
|
<meta name="color-scheme" content="light dark">
|
|
9
|
-
<title>thebird /
|
|
10
|
-
<meta name="description" content="thebird —
|
|
9
|
+
<title>thebird / web os</title>
|
|
10
|
+
<meta name="description" content="thebird — browser-native web OS. agentic chat, POSIX terminal, live preview, IDB filesystem. powered by acptoapi multi-provider bridge.">
|
|
11
11
|
<meta name="author" content="247420 / AnEntrypoint">
|
|
12
|
+
<meta name="keywords" content="thebird, web os, browser terminal, anthropic, gemini, openai, acptoapi, anentrypoint">
|
|
13
|
+
<link rel="canonical" href="https://anentrypoint.github.io/thebird/">
|
|
14
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='0' fill='%23247420'/><text x='16' y='22' font-family='monospace' font-size='13' font-weight='700' fill='%23EFE9DD' text-anchor='middle'>tb</text></svg>">
|
|
15
|
+
<meta property="og:type" content="website">
|
|
16
|
+
<meta property="og:title" content="thebird / web os">
|
|
17
|
+
<meta property="og:description" content="browser-native web OS — agentic chat, POSIX terminal, live preview. no server required.">
|
|
18
|
+
<meta property="og:url" content="https://anentrypoint.github.io/thebird/">
|
|
19
|
+
<meta property="og:site_name" content="247420 / thebird">
|
|
20
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
21
|
+
<meta name="twitter:site" content="@AnEntrypoint">
|
|
22
|
+
<meta name="robots" content="index, follow">
|
|
12
23
|
<link rel="stylesheet" href="vendor/xterm.css" />
|
|
13
24
|
<link rel="stylesheet" href="tui.css" />
|
|
25
|
+
<style>
|
|
26
|
+
@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=JetBrains+Mono:wght@400;500;600;700&display=swap');
|
|
27
|
+
|
|
28
|
+
/* ── landing shell ── */
|
|
29
|
+
.land {
|
|
30
|
+
background: var(--paper);
|
|
31
|
+
color: var(--ink);
|
|
32
|
+
font-family: var(--ff-mono);
|
|
33
|
+
font-size: 13px;
|
|
34
|
+
line-height: 1.5;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ── topbar ── */
|
|
38
|
+
.land-bar {
|
|
39
|
+
position: sticky;
|
|
40
|
+
top: 0;
|
|
41
|
+
z-index: 200;
|
|
42
|
+
background: var(--paper);
|
|
43
|
+
border-bottom: 1px solid var(--ink-hair);
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
padding: 0 2ch;
|
|
47
|
+
height: 40px;
|
|
48
|
+
gap: 2ch;
|
|
49
|
+
}
|
|
50
|
+
.land-bar .brand {
|
|
51
|
+
font-weight: 700;
|
|
52
|
+
font-size: 13px;
|
|
53
|
+
letter-spacing: 0.02em;
|
|
54
|
+
color: var(--ink);
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
}
|
|
58
|
+
.land-bar .slash { color: var(--ink-dim); padding: 0 0.3ch; font-weight: 400; }
|
|
59
|
+
.land-bar .leaf { color: var(--green); }
|
|
60
|
+
.land-bar nav { display: flex; gap: 0; margin-left: auto; }
|
|
61
|
+
.land-bar nav a {
|
|
62
|
+
color: var(--ink-dim);
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
padding: 0 1.5ch;
|
|
65
|
+
font-size: 12px;
|
|
66
|
+
line-height: 40px;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
letter-spacing: 0.04em;
|
|
69
|
+
text-transform: uppercase;
|
|
70
|
+
border-bottom: 2px solid transparent;
|
|
71
|
+
transition: color 80ms, border-color 80ms;
|
|
72
|
+
}
|
|
73
|
+
.land-bar nav a:hover { color: var(--ink); }
|
|
74
|
+
.land-bar nav a.active { color: var(--green); border-bottom-color: var(--green); }
|
|
75
|
+
.land-bar .theme-btn {
|
|
76
|
+
background: none;
|
|
77
|
+
border: none;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
color: var(--ink-dim);
|
|
80
|
+
font-size: 16px;
|
|
81
|
+
padding: 0 1ch;
|
|
82
|
+
line-height: 40px;
|
|
83
|
+
}
|
|
84
|
+
.land-bar .theme-btn:hover { color: var(--ink); }
|
|
85
|
+
|
|
86
|
+
/* ── hero ── */
|
|
87
|
+
.hero {
|
|
88
|
+
padding: 64px 2ch 48px 2ch;
|
|
89
|
+
border-bottom: 1px solid var(--ink-hair);
|
|
90
|
+
max-width: 1200px;
|
|
91
|
+
margin: 0 auto;
|
|
92
|
+
}
|
|
93
|
+
.hero-eyebrow {
|
|
94
|
+
font-size: 11px;
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
letter-spacing: 0.1em;
|
|
97
|
+
text-transform: uppercase;
|
|
98
|
+
color: var(--green);
|
|
99
|
+
margin-bottom: 16px;
|
|
100
|
+
}
|
|
101
|
+
.hero h1 {
|
|
102
|
+
font-family: var(--ff-display, 'Archivo Black', sans-serif);
|
|
103
|
+
font-size: clamp(36px, 6vw, 80px);
|
|
104
|
+
line-height: 1.0;
|
|
105
|
+
letter-spacing: -0.02em;
|
|
106
|
+
color: var(--ink);
|
|
107
|
+
margin: 0 0 24px 0;
|
|
108
|
+
max-width: 16ch;
|
|
109
|
+
}
|
|
110
|
+
.hero h1 em { font-style: normal; color: var(--green); }
|
|
111
|
+
.hero .lede {
|
|
112
|
+
font-size: 15px;
|
|
113
|
+
color: var(--ink-dim);
|
|
114
|
+
max-width: 62ch;
|
|
115
|
+
line-height: 1.6;
|
|
116
|
+
margin: 0 0 36px 0;
|
|
117
|
+
}
|
|
118
|
+
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
|
|
119
|
+
.btn-primary {
|
|
120
|
+
background: var(--green);
|
|
121
|
+
color: var(--green-fg, #fff);
|
|
122
|
+
font-family: var(--ff-mono);
|
|
123
|
+
font-weight: 700;
|
|
124
|
+
font-size: 12px;
|
|
125
|
+
letter-spacing: 0.06em;
|
|
126
|
+
text-transform: uppercase;
|
|
127
|
+
padding: 10px 20px;
|
|
128
|
+
text-decoration: none;
|
|
129
|
+
border: none;
|
|
130
|
+
cursor: pointer;
|
|
131
|
+
transition: opacity 80ms;
|
|
132
|
+
display: inline-block;
|
|
133
|
+
}
|
|
134
|
+
.btn-primary:hover { opacity: 0.85; }
|
|
135
|
+
.btn-ghost {
|
|
136
|
+
background: none;
|
|
137
|
+
color: var(--ink);
|
|
138
|
+
font-family: var(--ff-mono);
|
|
139
|
+
font-weight: 600;
|
|
140
|
+
font-size: 12px;
|
|
141
|
+
letter-spacing: 0.06em;
|
|
142
|
+
text-transform: uppercase;
|
|
143
|
+
padding: 10px 20px;
|
|
144
|
+
text-decoration: none;
|
|
145
|
+
border: 1px solid var(--ink-hair);
|
|
146
|
+
cursor: pointer;
|
|
147
|
+
transition: border-color 80ms, background 80ms;
|
|
148
|
+
display: inline-block;
|
|
149
|
+
}
|
|
150
|
+
.btn-ghost:hover { border-color: var(--ink); background: var(--surface-2); }
|
|
151
|
+
|
|
152
|
+
/* ── install strip ── */
|
|
153
|
+
.install-strip {
|
|
154
|
+
display: inline-flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
gap: 1ch;
|
|
157
|
+
background: var(--surface, rgba(11,11,9,0.04));
|
|
158
|
+
border: 1px solid var(--ink-hair);
|
|
159
|
+
padding: 8px 14px;
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
}
|
|
162
|
+
.install-strip .prompt { color: var(--green); font-weight: 700; }
|
|
163
|
+
.install-strip .cmd { color: var(--ink); }
|
|
164
|
+
.install-strip .copy-btn {
|
|
165
|
+
background: none; border: none; cursor: pointer;
|
|
166
|
+
color: var(--ink-dim); font-size: 11px; font-family: var(--ff-mono);
|
|
167
|
+
letter-spacing: 0.04em; text-transform: uppercase; padding: 0;
|
|
168
|
+
transition: color 80ms;
|
|
169
|
+
}
|
|
170
|
+
.install-strip .copy-btn:hover { color: var(--green); }
|
|
171
|
+
|
|
172
|
+
/* ── features grid ── */
|
|
173
|
+
.features {
|
|
174
|
+
padding: 48px 2ch;
|
|
175
|
+
max-width: 1200px;
|
|
176
|
+
margin: 0 auto;
|
|
177
|
+
}
|
|
178
|
+
.features-head {
|
|
179
|
+
font-size: 11px;
|
|
180
|
+
font-weight: 600;
|
|
181
|
+
letter-spacing: 0.1em;
|
|
182
|
+
text-transform: uppercase;
|
|
183
|
+
color: var(--ink-dim);
|
|
184
|
+
margin-bottom: 24px;
|
|
185
|
+
padding-bottom: 8px;
|
|
186
|
+
border-bottom: 1px solid var(--ink-hair);
|
|
187
|
+
}
|
|
188
|
+
.feat-grid {
|
|
189
|
+
display: grid;
|
|
190
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
191
|
+
gap: 0;
|
|
192
|
+
}
|
|
193
|
+
.feat-card {
|
|
194
|
+
padding: 24px;
|
|
195
|
+
border: 1px solid var(--ink-hair);
|
|
196
|
+
margin: -1px 0 0 -1px;
|
|
197
|
+
transition: background 80ms;
|
|
198
|
+
}
|
|
199
|
+
.feat-card:hover { background: var(--surface-2); }
|
|
200
|
+
.feat-card .glyph {
|
|
201
|
+
font-size: 20px;
|
|
202
|
+
margin-bottom: 12px;
|
|
203
|
+
display: block;
|
|
204
|
+
color: var(--green);
|
|
205
|
+
font-style: normal;
|
|
206
|
+
}
|
|
207
|
+
.feat-card h3 {
|
|
208
|
+
font-family: var(--ff-mono);
|
|
209
|
+
font-size: 13px;
|
|
210
|
+
font-weight: 700;
|
|
211
|
+
color: var(--ink);
|
|
212
|
+
margin: 0 0 8px 0;
|
|
213
|
+
text-transform: lowercase;
|
|
214
|
+
letter-spacing: 0.02em;
|
|
215
|
+
}
|
|
216
|
+
.feat-card p {
|
|
217
|
+
font-size: 12px;
|
|
218
|
+
color: var(--ink-dim);
|
|
219
|
+
line-height: 1.55;
|
|
220
|
+
margin: 0 0 12px 0;
|
|
221
|
+
}
|
|
222
|
+
.feat-tag {
|
|
223
|
+
display: inline-block;
|
|
224
|
+
font-size: 10px;
|
|
225
|
+
font-weight: 600;
|
|
226
|
+
letter-spacing: 0.08em;
|
|
227
|
+
text-transform: uppercase;
|
|
228
|
+
color: var(--green);
|
|
229
|
+
border: 1px solid var(--green);
|
|
230
|
+
padding: 1px 6px;
|
|
231
|
+
margin: 2px 2px 0 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* ── receipt panel ── */
|
|
235
|
+
.receipt-section {
|
|
236
|
+
padding: 0 2ch 48px 2ch;
|
|
237
|
+
max-width: 1200px;
|
|
238
|
+
margin: 0 auto;
|
|
239
|
+
display: grid;
|
|
240
|
+
grid-template-columns: 1fr 1fr;
|
|
241
|
+
gap: 32px;
|
|
242
|
+
align-items: start;
|
|
243
|
+
}
|
|
244
|
+
@media (max-width: 700px) { .receipt-section { grid-template-columns: 1fr; } }
|
|
245
|
+
.kv-panel {
|
|
246
|
+
border: 1px solid var(--ink-hair);
|
|
247
|
+
}
|
|
248
|
+
.kv-panel .panel-head {
|
|
249
|
+
border-bottom: 1px solid var(--ink-hair);
|
|
250
|
+
padding: 8px 16px;
|
|
251
|
+
font-size: 11px;
|
|
252
|
+
font-weight: 700;
|
|
253
|
+
letter-spacing: 0.08em;
|
|
254
|
+
text-transform: uppercase;
|
|
255
|
+
color: var(--ink-dim);
|
|
256
|
+
display: flex;
|
|
257
|
+
justify-content: space-between;
|
|
258
|
+
}
|
|
259
|
+
.kv-panel table { width: 100%; border-collapse: collapse; }
|
|
260
|
+
.kv-panel td {
|
|
261
|
+
padding: 7px 16px;
|
|
262
|
+
font-size: 12px;
|
|
263
|
+
border-bottom: 1px solid var(--ink-hair);
|
|
264
|
+
vertical-align: top;
|
|
265
|
+
}
|
|
266
|
+
.kv-panel td:first-child { color: var(--ink-dim); width: 40%; white-space: nowrap; }
|
|
267
|
+
.kv-panel td:last-child { color: var(--ink); font-weight: 500; }
|
|
268
|
+
.kv-panel tr:last-child td { border-bottom: 0; }
|
|
269
|
+
|
|
270
|
+
/* ── providers strip ── */
|
|
271
|
+
.providers {
|
|
272
|
+
display: flex; flex-wrap: wrap; gap: 8px;
|
|
273
|
+
}
|
|
274
|
+
.chip {
|
|
275
|
+
font-size: 10px;
|
|
276
|
+
font-weight: 700;
|
|
277
|
+
letter-spacing: 0.06em;
|
|
278
|
+
text-transform: uppercase;
|
|
279
|
+
padding: 3px 8px;
|
|
280
|
+
border: 1px solid var(--ink-hair);
|
|
281
|
+
color: var(--ink-dim);
|
|
282
|
+
white-space: nowrap;
|
|
283
|
+
}
|
|
284
|
+
.chip.green { border-color: var(--green); color: var(--green); }
|
|
285
|
+
|
|
286
|
+
/* ── architecture diagram ── */
|
|
287
|
+
.arch {
|
|
288
|
+
background: var(--surface, rgba(11,11,9,0.04));
|
|
289
|
+
border: 1px solid var(--ink-hair);
|
|
290
|
+
padding: 20px;
|
|
291
|
+
font-size: 12px;
|
|
292
|
+
line-height: 1.7;
|
|
293
|
+
white-space: pre;
|
|
294
|
+
overflow-x: auto;
|
|
295
|
+
font-family: var(--ff-mono);
|
|
296
|
+
color: var(--ink);
|
|
297
|
+
}
|
|
298
|
+
.arch .hl { color: var(--green); font-weight: 700; }
|
|
299
|
+
|
|
300
|
+
/* ── live app section ── */
|
|
301
|
+
.app-section {
|
|
302
|
+
border-top: 2px solid var(--ink);
|
|
303
|
+
background: var(--paper);
|
|
304
|
+
}
|
|
305
|
+
.app-section-head {
|
|
306
|
+
padding: 0 2ch;
|
|
307
|
+
height: 40px;
|
|
308
|
+
background: var(--paper);
|
|
309
|
+
border-bottom: 1px solid var(--ink-hair);
|
|
310
|
+
display: flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
gap: 2ch;
|
|
313
|
+
font-size: 11px;
|
|
314
|
+
font-weight: 700;
|
|
315
|
+
letter-spacing: 0.08em;
|
|
316
|
+
text-transform: uppercase;
|
|
317
|
+
color: var(--ink-dim);
|
|
318
|
+
}
|
|
319
|
+
.app-section-head .live-dot {
|
|
320
|
+
width: 6px; height: 6px;
|
|
321
|
+
border-radius: 50%;
|
|
322
|
+
background: var(--green);
|
|
323
|
+
display: inline-block;
|
|
324
|
+
animation: pulse 2s infinite;
|
|
325
|
+
}
|
|
326
|
+
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
|
|
327
|
+
|
|
328
|
+
/* ── live app embed ── */
|
|
329
|
+
.app-embed {
|
|
330
|
+
display: flex;
|
|
331
|
+
flex-direction: column;
|
|
332
|
+
height: 85vh;
|
|
333
|
+
min-height: 500px;
|
|
334
|
+
}
|
|
335
|
+
</style>
|
|
14
336
|
<script>
|
|
15
337
|
(() => {
|
|
16
338
|
const saved = localStorage.getItem('tui_theme');
|
|
@@ -29,31 +351,235 @@
|
|
|
29
351
|
</script>
|
|
30
352
|
</head>
|
|
31
353
|
<body>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
354
|
+
|
|
355
|
+
<!-- ── LANDING ── -->
|
|
356
|
+
<div class="land">
|
|
357
|
+
|
|
358
|
+
<!-- topbar -->
|
|
359
|
+
<header class="land-bar">
|
|
360
|
+
<span class="brand">
|
|
361
|
+
247420<span class="slash">/</span>thebird
|
|
362
|
+
</span>
|
|
363
|
+
<nav>
|
|
364
|
+
<a href="#features" class="active">features</a>
|
|
365
|
+
<a href="#live">live app</a>
|
|
366
|
+
<a href="https://github.com/AnEntrypoint/thebird">source ↗</a>
|
|
367
|
+
</nav>
|
|
368
|
+
<button class="theme-btn" onclick="toggleTheme()" title="Toggle theme">◐</button>
|
|
369
|
+
</header>
|
|
370
|
+
|
|
371
|
+
<!-- hero -->
|
|
372
|
+
<section class="hero">
|
|
373
|
+
<div class="hero-eyebrow">247420 / anentrypoint</div>
|
|
374
|
+
<h1>web os.<br>in the <em>browser.</em></h1>
|
|
375
|
+
<p class="lede">
|
|
376
|
+
thebird is a browser-native operating system — agentic chat, a full POSIX terminal,
|
|
377
|
+
live preview, and an IndexedDB filesystem. no server required. api key stays in
|
|
378
|
+
localStorage. powered by <strong>acptoapi</strong>: any anthropic-format message
|
|
379
|
+
to any provider, in-buffer, zero ports.
|
|
380
|
+
</p>
|
|
381
|
+
<div class="hero-actions">
|
|
382
|
+
<a href="#live" class="btn-primary">try it live</a>
|
|
383
|
+
<a href="https://github.com/AnEntrypoint/thebird" class="btn-ghost">source ↗</a>
|
|
384
|
+
<div class="install-strip" id="install-strip">
|
|
385
|
+
<span class="prompt">$</span>
|
|
386
|
+
<span class="cmd">npm install acptoapi</span>
|
|
387
|
+
<button class="copy-btn" onclick="copyInstall()">copy</button>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</section>
|
|
391
|
+
|
|
392
|
+
<!-- features grid -->
|
|
393
|
+
<section class="features" id="features">
|
|
394
|
+
<div class="features-head">capabilities</div>
|
|
395
|
+
<div class="feat-grid">
|
|
396
|
+
|
|
397
|
+
<div class="feat-card">
|
|
398
|
+
<em class="glyph">⬡</em>
|
|
399
|
+
<h3>agentic chat</h3>
|
|
400
|
+
<p>tool-calling chat via acptoapi running entirely in-browser. tools: read_file, write_file, list_files (idb-backed), run_command, read_terminal, send_to_terminal. no proxy server required.</p>
|
|
401
|
+
<span class="feat-tag">in-browser</span>
|
|
402
|
+
<span class="feat-tag">tool-use</span>
|
|
403
|
+
<span class="feat-tag">streaming</span>
|
|
404
|
+
</div>
|
|
405
|
+
|
|
406
|
+
<div class="feat-card">
|
|
407
|
+
<em class="glyph">▶</em>
|
|
408
|
+
<h3>posix terminal</h3>
|
|
409
|
+
<p>browser-native shell (xstate v5 state machine, v8 eval) backed by IndexedDB filesystem. built-in: ls, cat, cd, pwd, mkdir, rm, cp, mv, echo, env, export, node, npm install, grep, sed, awk, sort, uniq, tr.</p>
|
|
410
|
+
<span class="feat-tag">posix</span>
|
|
411
|
+
<span class="feat-tag">node repl</span>
|
|
412
|
+
<span class="feat-tag">idb-fs</span>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<div class="feat-card">
|
|
416
|
+
<em class="glyph">◈</em>
|
|
417
|
+
<h3>live preview</h3>
|
|
418
|
+
<p>iframe served by a service worker reading files from IndexedDB at /preview/*. hot-reloads 5s after any file write. build and run html/css/js apps entirely in-browser.</p>
|
|
419
|
+
<span class="feat-tag">service worker</span>
|
|
420
|
+
<span class="feat-tag">hot reload</span>
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
<div class="feat-card">
|
|
424
|
+
<em class="glyph">⟐</em>
|
|
425
|
+
<h3>acptoapi bridge</h3>
|
|
426
|
+
<p>translate any anthropic-format message to gemini, openai-compat, or any configured provider. format registry (anthropic/openai/gemini/acp) × provider registry (gemini/openai-compat/router). normalized event stream.</p>
|
|
427
|
+
<span class="feat-tag">multi-provider</span>
|
|
428
|
+
<span class="feat-tag">streaming</span>
|
|
429
|
+
<span class="feat-tag">zero-config</span>
|
|
430
|
+
</div>
|
|
431
|
+
|
|
432
|
+
<div class="feat-card">
|
|
433
|
+
<em class="glyph">◧</em>
|
|
434
|
+
<h3>idb filesystem</h3>
|
|
435
|
+
<p>persistent IndexedDB-backed virtual filesystem. files survive page reloads. node_modules can be installed and require()'d. http.createServer polyfill for running express-style servers in-browser.</p>
|
|
436
|
+
<span class="feat-tag">persistent</span>
|
|
437
|
+
<span class="feat-tag">node compat</span>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
<div class="feat-card">
|
|
441
|
+
<em class="glyph">⊞</em>
|
|
442
|
+
<h3>sdk clients</h3>
|
|
443
|
+
<p>drop-in Anthropic and OpenAI sdk clients via acptoapi. new Anthropic({provider,apiKey}) and new OpenAI({baseURL,apiKey}) backed by any provider. xstate machine for streaming orchestration. flowie pipelines for transforms.</p>
|
|
444
|
+
<span class="feat-tag">anthropic sdk</span>
|
|
445
|
+
<span class="feat-tag">openai sdk</span>
|
|
446
|
+
<span class="feat-tag">xstate</span>
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<div class="feat-card">
|
|
450
|
+
<em class="glyph">⚙</em>
|
|
451
|
+
<h3>http proxy server</h3>
|
|
452
|
+
<p>server.js exposes a local anthropic-compatible proxy at localhost:3456 backed by acptoapi. route /v1/messages to any provider. /debug/state endpoint for live circuit-breaker and provider state inspection.</p>
|
|
453
|
+
<span class="feat-tag">local proxy</span>
|
|
454
|
+
<span class="feat-tag">observability</span>
|
|
455
|
+
</div>
|
|
456
|
+
|
|
457
|
+
<div class="feat-card">
|
|
458
|
+
<em class="glyph">◎</em>
|
|
459
|
+
<h3>multi-provider routing</h3>
|
|
460
|
+
<p>createRouter() picks provider+model per request based on taskType, token count vs longContextThreshold. circuit breaker, capability registry, stream guards, retry with backoff. gemini, openai-compat, 20+ providers.</p>
|
|
461
|
+
<span class="feat-tag">routing</span>
|
|
462
|
+
<span class="feat-tag">circuit breaker</span>
|
|
463
|
+
<span class="feat-tag">retry</span>
|
|
464
|
+
</div>
|
|
465
|
+
|
|
466
|
+
</div>
|
|
467
|
+
</section>
|
|
468
|
+
|
|
469
|
+
<!-- receipt + architecture -->
|
|
470
|
+
<section class="receipt-section">
|
|
471
|
+
<div class="kv-panel">
|
|
472
|
+
<div class="panel-head"><span>project receipt</span><span style="color:var(--green)">● live</span></div>
|
|
473
|
+
<table>
|
|
474
|
+
<tr><td>status</td><td>live · ships continuously</td></tr>
|
|
475
|
+
<tr><td>license</td><td>MIT</td></tr>
|
|
476
|
+
<tr><td>runtime</td><td>browser + node ≥18</td></tr>
|
|
477
|
+
<tr><td>deps</td><td>acptoapi, @google/genai, xstate, flowie</td></tr>
|
|
478
|
+
<tr><td>frontend</td><td>no framework · vanilla js + webjsx</td></tr>
|
|
479
|
+
<tr><td>storage</td><td>IndexedDB (no server, no cloud)</td></tr>
|
|
480
|
+
<tr><td>api key</td><td>localStorage only · never leaves browser</td></tr>
|
|
481
|
+
<tr><td>providers</td>
|
|
482
|
+
<td>
|
|
483
|
+
<div class="providers">
|
|
484
|
+
<span class="chip green">gemini</span>
|
|
485
|
+
<span class="chip">openai</span>
|
|
486
|
+
<span class="chip">together</span>
|
|
487
|
+
<span class="chip">fireworks</span>
|
|
488
|
+
<span class="chip">perplexity</span>
|
|
489
|
+
<span class="chip">groq</span>
|
|
490
|
+
<span class="chip">ollama</span>
|
|
491
|
+
<span class="chip">openrouter</span>
|
|
492
|
+
<span class="chip">20+ more</span>
|
|
493
|
+
</div>
|
|
494
|
+
</td>
|
|
495
|
+
</tr>
|
|
496
|
+
</table>
|
|
52
497
|
</div>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
498
|
+
|
|
499
|
+
<div>
|
|
500
|
+
<div class="kv-panel" style="margin-bottom:24px">
|
|
501
|
+
<div class="panel-head"><span>architecture</span></div>
|
|
502
|
+
<pre class="arch" style="border:0;margin:0"><span class="hl">thebird</span> (web os shell)
|
|
503
|
+
├── docs/ browser UI
|
|
504
|
+
│ ├── chat agentic chat + tool calling
|
|
505
|
+
│ ├── terminal posix shell · idb filesystem
|
|
506
|
+
│ └── preview service worker · hot reload
|
|
507
|
+
├── serve.js static file server
|
|
508
|
+
└── server.js anthropic-compat proxy
|
|
509
|
+
|
|
510
|
+
<span class="hl">acptoapi</span> (npm dep)
|
|
511
|
+
├── lib/formats/ anthropic · openai · gemini · acp
|
|
512
|
+
├── lib/providers/ gemini · openai-compat · router
|
|
513
|
+
├── lib/machine.js xstate streaming actor
|
|
514
|
+
├── lib/translate any → any in-buffer
|
|
515
|
+
└── lib/sdk/ Anthropic + OpenAI clients</pre>
|
|
516
|
+
</div>
|
|
517
|
+
|
|
518
|
+
<div class="kv-panel">
|
|
519
|
+
<div class="panel-head"><span>quick start</span></div>
|
|
520
|
+
<table>
|
|
521
|
+
<tr><td>local dev</td><td><code>node serve.js</code> → localhost:8080</td></tr>
|
|
522
|
+
<tr><td>proxy</td><td><code>node server.js</code> → localhost:3456</td></tr>
|
|
523
|
+
<tr><td>acptoapi sdk</td><td><code>npm install acptoapi</code></td></tr>
|
|
524
|
+
<tr><td>source</td><td><a href="https://github.com/AnEntrypoint/thebird" style="color:var(--green)">github.com/AnEntrypoint/thebird ↗</a></td></tr>
|
|
525
|
+
</table>
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
</section>
|
|
529
|
+
|
|
530
|
+
<!-- live app section -->
|
|
531
|
+
<section class="app-section" id="live">
|
|
532
|
+
<div class="app-section-head">
|
|
533
|
+
<span class="live-dot"></span>
|
|
534
|
+
<span>live app — running in your browser</span>
|
|
535
|
+
<span style="margin-left:auto;font-size:10px;color:var(--ink-dim)">api key stored in localStorage · no server</span>
|
|
536
|
+
</div>
|
|
537
|
+
|
|
538
|
+
<div class="app-embed">
|
|
539
|
+
<div class="tui-tabs">
|
|
540
|
+
<button id="tab-chat" class="tui-tab active" onclick="switchTab('chat')">chat</button>
|
|
541
|
+
<button id="tab-term" class="tui-tab" onclick="switchTab('term')">terminal</button>
|
|
542
|
+
<button id="tab-preview" class="tui-tab" onclick="switchTab('preview')">preview</button>
|
|
543
|
+
</div>
|
|
544
|
+
<div id="pane-chat" style="flex:1;overflow:hidden;display:flex;flex-direction:column">
|
|
545
|
+
<bird-chat></bird-chat>
|
|
546
|
+
</div>
|
|
547
|
+
<div id="pane-term" class="hidden" style="flex:1;overflow:hidden;display:flex;flex-direction:column">
|
|
548
|
+
<div id="term-container" style="flex:1"></div>
|
|
549
|
+
</div>
|
|
550
|
+
<div id="pane-preview" class="hidden" style="flex:1;overflow:hidden;display:flex;flex-direction:column">
|
|
551
|
+
<div class="tui-toolbar">
|
|
552
|
+
<button class="tui-btn" onclick="refreshPreview()">[reload]</button>
|
|
553
|
+
</div>
|
|
554
|
+
<iframe id="preview-frame" style="width:100%;flex:1;border:0;background:var(--paper)" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
</section>
|
|
558
|
+
|
|
559
|
+
<!-- footer -->
|
|
560
|
+
<footer style="border-top:1px solid var(--ink-hair);padding:16px 2ch;display:flex;gap:2ch;align-items:center;font-size:11px;color:var(--ink-dim)">
|
|
561
|
+
<span>247420 / anentrypoint</span>
|
|
562
|
+
<span style="color:var(--ink-hair)">·</span>
|
|
563
|
+
<span>thebird</span>
|
|
564
|
+
<span style="color:var(--ink-hair)">·</span>
|
|
565
|
+
<a href="https://github.com/AnEntrypoint/thebird" style="color:var(--ink-dim);text-decoration:none">source ↗</a>
|
|
566
|
+
<span style="color:var(--ink-hair)">·</span>
|
|
567
|
+
<a href="https://github.com/AnEntrypoint/acptoapi" style="color:var(--ink-dim);text-decoration:none">acptoapi ↗</a>
|
|
568
|
+
<span style="margin-left:auto;color:var(--green)">probably emerging 🌀</span>
|
|
569
|
+
</footer>
|
|
570
|
+
|
|
571
|
+
</div><!-- /land -->
|
|
572
|
+
|
|
56
573
|
<script>
|
|
574
|
+
function copyInstall() {
|
|
575
|
+
navigator.clipboard?.writeText('npm install acptoapi');
|
|
576
|
+
const btn = document.querySelector('#install-strip .copy-btn');
|
|
577
|
+
if (!btn) return;
|
|
578
|
+
const orig = btn.textContent;
|
|
579
|
+
btn.textContent = 'copied';
|
|
580
|
+
setTimeout(() => { btn.textContent = orig; }, 1200);
|
|
581
|
+
}
|
|
582
|
+
|
|
57
583
|
function callExpressRoute(path, method) {
|
|
58
584
|
const handlers = window.__debug?.shell?.httpHandlers || {};
|
|
59
585
|
const app = Object.values(handlers)[0];
|
|
@@ -82,6 +608,7 @@ function callExpressRoute(path, method) {
|
|
|
82
608
|
setTimeout(() => finish(res._status, res._body, res._ct), 5000);
|
|
83
609
|
});
|
|
84
610
|
}
|
|
611
|
+
|
|
85
612
|
async function refreshPreview() {
|
|
86
613
|
const iframe = document.getElementById('preview-frame');
|
|
87
614
|
if (!iframe) return;
|
|
@@ -104,6 +631,7 @@ async function refreshPreview() {
|
|
|
104
631
|
const fg = cs.getPropertyValue('--ink').trim(), bg = cs.getPropertyValue('--paper').trim();
|
|
105
632
|
iframe.srcdoc = `<pre style="color:${fg};background:${bg};padding:1ch;font-family:monospace">no files yet</pre>`;
|
|
106
633
|
}
|
|
634
|
+
|
|
107
635
|
function switchTab(t) {
|
|
108
636
|
['chat', 'term', 'preview'].forEach(id => {
|
|
109
637
|
document.getElementById('pane-' + id).classList.toggle('hidden', id !== t);
|
package/package.json
CHANGED