thebird 1.2.106 → 1.2.108

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 CHANGED
@@ -1 +1 @@
1
- gm:gm-execute
1
+ gm:gm-complete
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 2026-04-24
2
+ - feat(docs): full 247420-design landing page with hero, 8-feature grid, architecture diagram, project receipt, and embedded live app
3
+ - refactor: acptoapi extracted to separate package; thebird re-exports it via index.js
4
+
1
5
 
2
6
  ## [unreleased] 2026-04-21 theme system + richer chat output
3
7
  - feat: dark/light theme via [data-theme] attribute; pre-paint boot script respects localStorage + prefers-color-scheme
package/docs/index.html CHANGED
@@ -7,14 +7,14 @@
7
7
  <meta name="theme-color" content="#3FA93A" media="(prefers-color-scheme: dark)">
8
8
  <meta name="color-scheme" content="light dark">
9
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.">
10
+ <meta name="description" content="thebird — browser-native web OS. agentic AI, POSIX terminal, live preview, IDB filesystem. serverless. no Docker. no server. powered by acptoapi.">
11
11
  <meta name="author" content="247420 / AnEntrypoint">
12
- <meta name="keywords" content="thebird, web os, browser terminal, anthropic, gemini, openai, acptoapi, anentrypoint">
12
+ <meta name="keywords" content="thebird, web os, browser terminal, anthropic, gemini, openai, acptoapi, serverless, anentrypoint">
13
13
  <link rel="canonical" href="https://anentrypoint.github.io/thebird/">
14
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
15
  <meta property="og:type" content="website">
16
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.">
17
+ <meta property="og:description" content="browser-native web OS — agentic AI, POSIX terminal, live preview. no server. no Docker. serverless.">
18
18
  <meta property="og:url" content="https://anentrypoint.github.io/thebird/">
19
19
  <meta property="og:site_name" content="247420 / thebird">
20
20
  <meta name="twitter:card" content="summary_large_image">
@@ -25,13 +25,15 @@
25
25
  <style>
26
26
  @import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=JetBrains+Mono:wght@400;500;600;700&display=swap');
27
27
 
28
- /* ── landing shell ── */
29
28
  .land {
30
29
  background: var(--paper);
31
30
  color: var(--ink);
32
31
  font-family: var(--ff-mono);
33
32
  font-size: 13px;
34
33
  line-height: 1.5;
34
+ min-height: 100vh;
35
+ display: flex;
36
+ flex-direction: column;
35
37
  }
36
38
 
37
39
  /* ── topbar ── */
@@ -45,33 +47,67 @@
45
47
  align-items: center;
46
48
  padding: 0 2ch;
47
49
  height: 40px;
48
- gap: 2ch;
50
+ gap: 0;
49
51
  }
50
52
  .land-bar .brand {
51
53
  font-weight: 700;
52
54
  font-size: 13px;
53
55
  letter-spacing: 0.02em;
54
56
  color: var(--ink);
57
+ margin-right: 2ch;
58
+ flex-shrink: 0;
59
+ }
60
+ .land-bar .slash { color: var(--ink-dim); padding: 0 0.3ch; font-weight: 400; }
61
+ .land-bar .brand-tabs {
62
+ display: flex;
63
+ height: 40px;
64
+ gap: 0;
65
+ }
66
+ .brand-tab {
67
+ background: none;
68
+ border: none;
69
+ border-bottom: 2px solid transparent;
70
+ cursor: pointer;
71
+ color: var(--ink-dim);
72
+ font-family: var(--ff-mono);
73
+ font-size: 12px;
74
+ font-weight: 600;
75
+ letter-spacing: 0.04em;
76
+ text-transform: uppercase;
77
+ padding: 0 1.5ch;
78
+ height: 40px;
79
+ transition: color 80ms, border-color 80ms;
80
+ white-space: nowrap;
81
+ }
82
+ .brand-tab:hover { color: var(--ink); }
83
+ .brand-tab.active { color: var(--green); border-bottom-color: var(--green); }
84
+ .brand-tab .live-dot {
85
+ display: inline-block;
86
+ width: 5px; height: 5px;
87
+ border-radius: 50%;
88
+ background: var(--green);
89
+ margin-left: 6px;
90
+ vertical-align: middle;
91
+ animation: pulse 2s infinite;
92
+ }
93
+ .land-bar .bar-right {
94
+ margin-left: auto;
55
95
  display: flex;
56
96
  align-items: center;
97
+ gap: 0;
57
98
  }
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 {
99
+ .land-bar .bar-right a {
62
100
  color: var(--ink-dim);
63
101
  text-decoration: none;
64
- padding: 0 1.5ch;
65
102
  font-size: 12px;
66
- line-height: 40px;
67
103
  font-weight: 600;
68
104
  letter-spacing: 0.04em;
69
105
  text-transform: uppercase;
70
- border-bottom: 2px solid transparent;
71
- transition: color 80ms, border-color 80ms;
106
+ padding: 0 1.5ch;
107
+ line-height: 40px;
108
+ transition: color 80ms;
72
109
  }
73
- .land-bar nav a:hover { color: var(--ink); }
74
- .land-bar nav a.active { color: var(--green); border-bottom-color: var(--green); }
110
+ .land-bar .bar-right a:hover { color: var(--ink); }
75
111
  .land-bar .theme-btn {
76
112
  background: none;
77
113
  border: none;
@@ -82,6 +118,14 @@
82
118
  line-height: 40px;
83
119
  }
84
120
  .land-bar .theme-btn:hover { color: var(--ink); }
121
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
122
+
123
+ /* ── page panels ── */
124
+ .page-panel { display: none; flex: 1; flex-direction: column; }
125
+ .page-panel.active { display: flex; }
126
+
127
+ /* ── overview page ── */
128
+ #page-overview { overflow-y: auto; }
85
129
 
86
130
  /* ── hero ── */
87
131
  .hero {
@@ -89,6 +133,8 @@
89
133
  border-bottom: 1px solid var(--ink-hair);
90
134
  max-width: 1200px;
91
135
  margin: 0 auto;
136
+ width: 100%;
137
+ box-sizing: border-box;
92
138
  }
93
139
  .hero-eyebrow {
94
140
  font-size: 11px;
@@ -105,16 +151,26 @@
105
151
  letter-spacing: -0.02em;
106
152
  color: var(--ink);
107
153
  margin: 0 0 24px 0;
108
- max-width: 16ch;
154
+ max-width: 18ch;
109
155
  }
110
156
  .hero h1 em { font-style: normal; color: var(--green); }
111
157
  .hero .lede {
112
158
  font-size: 15px;
113
159
  color: var(--ink-dim);
114
- max-width: 62ch;
160
+ max-width: 66ch;
161
+ line-height: 1.65;
162
+ margin: 0 0 12px 0;
163
+ }
164
+ .hero .motivation {
165
+ font-size: 13px;
166
+ color: var(--ink-dim);
167
+ max-width: 66ch;
115
168
  line-height: 1.6;
116
- margin: 0 0 36px 0;
169
+ margin: 0 0 32px 0;
170
+ border-left: 2px solid var(--green);
171
+ padding-left: 1.5ch;
117
172
  }
173
+ .hero .motivation strong { color: var(--ink); }
118
174
  .hero-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
119
175
  .btn-primary {
120
176
  background: var(--green);
@@ -148,8 +204,6 @@
148
204
  display: inline-block;
149
205
  }
150
206
  .btn-ghost:hover { border-color: var(--ink); background: var(--surface-2); }
151
-
152
- /* ── install strip ── */
153
207
  .install-strip {
154
208
  display: inline-flex;
155
209
  align-items: center;
@@ -160,7 +214,6 @@
160
214
  font-size: 12px;
161
215
  }
162
216
  .install-strip .prompt { color: var(--green); font-weight: 700; }
163
- .install-strip .cmd { color: var(--ink); }
164
217
  .install-strip .copy-btn {
165
218
  background: none; border: none; cursor: pointer;
166
219
  color: var(--ink-dim); font-size: 11px; font-family: var(--ff-mono);
@@ -169,21 +222,66 @@
169
222
  }
170
223
  .install-strip .copy-btn:hover { color: var(--green); }
171
224
 
225
+ /* ── serverless callout ── */
226
+ .serverless-strip {
227
+ background: var(--surface, rgba(11,11,9,0.04));
228
+ border-top: 1px solid var(--ink-hair);
229
+ border-bottom: 1px solid var(--ink-hair);
230
+ padding: 20px 2ch;
231
+ }
232
+ .serverless-strip .inner {
233
+ max-width: 1200px;
234
+ margin: 0 auto;
235
+ display: grid;
236
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
237
+ gap: 0;
238
+ }
239
+ .sl-item {
240
+ padding: 16px 20px;
241
+ border-right: 1px solid var(--ink-hair);
242
+ }
243
+ .sl-item:last-child { border-right: 0; }
244
+ .sl-item .sl-icon { font-size: 18px; display: block; margin-bottom: 6px; color: var(--green); }
245
+ .sl-item .sl-label {
246
+ font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
247
+ color: var(--ink); margin-bottom: 4px;
248
+ }
249
+ .sl-item .sl-desc { font-size: 11px; color: var(--ink-dim); line-height: 1.5; }
250
+ @media (max-width: 600px) { .sl-item { border-right: 0; border-bottom: 1px solid var(--ink-hair); } }
251
+
252
+ /* ── motivation section ── */
253
+ .motivation-section {
254
+ padding: 48px 2ch 40px 2ch;
255
+ max-width: 1200px;
256
+ margin: 0 auto;
257
+ width: 100%;
258
+ box-sizing: border-box;
259
+ border-bottom: 1px solid var(--ink-hair);
260
+ }
261
+ .section-head {
262
+ font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
263
+ color: var(--ink-dim); margin-bottom: 24px; padding-bottom: 8px;
264
+ border-bottom: 1px solid var(--ink-hair);
265
+ }
266
+ .mot-grid {
267
+ display: grid;
268
+ grid-template-columns: 1fr 1fr;
269
+ gap: 32px;
270
+ }
271
+ @media (max-width: 700px) { .mot-grid { grid-template-columns: 1fr; } }
272
+ .mot-block h3 {
273
+ font-size: 13px; font-weight: 700; color: var(--ink); margin: 0 0 8px 0;
274
+ text-transform: lowercase; letter-spacing: 0.02em;
275
+ }
276
+ .mot-block p { font-size: 12px; color: var(--ink-dim); line-height: 1.6; margin: 0; }
277
+
172
278
  /* ── features grid ── */
173
279
  .features {
174
280
  padding: 48px 2ch;
175
281
  max-width: 1200px;
176
282
  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);
283
+ width: 100%;
284
+ box-sizing: border-box;
187
285
  }
188
286
  .feat-grid {
189
287
  display: grid;
@@ -198,37 +296,17 @@
198
296
  }
199
297
  .feat-card:hover { background: var(--surface-2); }
200
298
  .feat-card .glyph {
201
- font-size: 20px;
202
- margin-bottom: 12px;
203
- display: block;
204
- color: var(--green);
205
- font-style: normal;
299
+ font-size: 20px; margin-bottom: 12px; display: block; color: var(--green); font-style: normal;
206
300
  }
207
301
  .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;
302
+ font-family: var(--ff-mono); font-size: 13px; font-weight: 700; color: var(--ink);
303
+ margin: 0 0 8px 0; text-transform: lowercase; letter-spacing: 0.02em;
221
304
  }
305
+ .feat-card p { font-size: 12px; color: var(--ink-dim); line-height: 1.55; margin: 0 0 12px 0; }
222
306
  .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;
307
+ display: inline-block; font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
308
+ text-transform: uppercase; color: var(--green); border: 1px solid var(--green);
309
+ padding: 1px 6px; margin: 2px 2px 0 0;
232
310
  }
233
311
 
234
312
  /* ── receipt panel ── */
@@ -236,101 +314,49 @@
236
314
  padding: 0 2ch 48px 2ch;
237
315
  max-width: 1200px;
238
316
  margin: 0 auto;
317
+ width: 100%;
318
+ box-sizing: border-box;
239
319
  display: grid;
240
320
  grid-template-columns: 1fr 1fr;
241
321
  gap: 32px;
242
322
  align-items: start;
243
323
  }
244
324
  @media (max-width: 700px) { .receipt-section { grid-template-columns: 1fr; } }
245
- .kv-panel {
246
- border: 1px solid var(--ink-hair);
247
- }
325
+ .kv-panel { border: 1px solid var(--ink-hair); }
248
326
  .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;
327
+ border-bottom: 1px solid var(--ink-hair); padding: 8px 16px;
328
+ font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
329
+ color: var(--ink-dim); display: flex; justify-content: space-between;
258
330
  }
259
331
  .kv-panel table { width: 100%; border-collapse: collapse; }
260
332
  .kv-panel td {
261
- padding: 7px 16px;
262
- font-size: 12px;
263
- border-bottom: 1px solid var(--ink-hair);
264
- vertical-align: top;
333
+ padding: 7px 16px; font-size: 12px; border-bottom: 1px solid var(--ink-hair); vertical-align: top;
265
334
  }
266
335
  .kv-panel td:first-child { color: var(--ink-dim); width: 40%; white-space: nowrap; }
267
336
  .kv-panel td:last-child { color: var(--ink); font-weight: 500; }
268
337
  .kv-panel tr:last-child td { border-bottom: 0; }
269
-
270
- /* ── providers strip ── */
271
- .providers {
272
- display: flex; flex-wrap: wrap; gap: 8px;
273
- }
338
+ .providers { display: flex; flex-wrap: wrap; gap: 8px; }
274
339
  .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;
340
+ font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
341
+ padding: 3px 8px; border: 1px solid var(--ink-hair); color: var(--ink-dim); white-space: nowrap;
283
342
  }
284
343
  .chip.green { border-color: var(--green); color: var(--green); }
285
-
286
- /* ── architecture diagram ── */
287
344
  .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);
345
+ background: var(--surface, rgba(11,11,9,0.04)); border: 1px solid var(--ink-hair);
346
+ padding: 20px; font-size: 12px; line-height: 1.7; white-space: pre; overflow-x: auto;
347
+ font-family: var(--ff-mono); color: var(--ink);
297
348
  }
298
349
  .arch .hl { color: var(--green); font-weight: 700; }
299
350
 
300
- /* ── live app section ── */
301
- .app-section {
302
- border-top: 2px solid var(--ink);
303
- background: var(--paper);
351
+ /* ── live app panel ── */
352
+ #page-app {
353
+ overflow: hidden;
304
354
  }
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
355
  .app-embed {
330
356
  display: flex;
331
357
  flex-direction: column;
332
- height: 85vh;
333
- min-height: 500px;
358
+ flex: 1;
359
+ overflow: hidden;
334
360
  }
335
361
  </style>
336
362
  <script>
@@ -352,194 +378,274 @@
352
378
  </head>
353
379
  <body>
354
380
 
355
- <!-- ── LANDING ── -->
356
381
  <div class="land">
357
382
 
358
- <!-- topbar -->
383
+ <!-- topbar with top-level tabs -->
359
384
  <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>
385
+ <span class="brand">247420<span class="slash">/</span>thebird</span>
386
+ <div class="brand-tabs">
387
+ <button class="brand-tab active" id="ptab-overview" onclick="switchPage('overview')">overview</button>
388
+ <button class="brand-tab" id="ptab-app" onclick="switchPage('app')">live app <span class="live-dot"></span></button>
389
+ </div>
390
+ <div class="bar-right">
366
391
  <a href="https://github.com/AnEntrypoint/thebird">source ↗</a>
367
- </nav>
368
- <button class="theme-btn" onclick="toggleTheme()" title="Toggle theme">◐</button>
392
+ <button class="theme-btn" onclick="toggleTheme()" title="Toggle theme">◐</button>
393
+ </div>
369
394
  </header>
370
395
 
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>
396
+ <!-- OVERVIEW PAGE -->
397
+ <div class="page-panel active" id="page-overview">
398
+
399
+ <!-- hero -->
400
+ <section class="hero">
401
+ <div class="hero-eyebrow">247420 / anentrypoint</div>
402
+ <h1>agentic os.<br>zero <em>server.</em></h1>
403
+ <p class="lede">
404
+ thebird is a browser-native operating system — agentic AI chat, a full POSIX terminal,
405
+ live preview, and an IndexedDB filesystem. loads in the browser like a webpage.
406
+ no Docker. no server. no install.
407
+ </p>
408
+ <p class="motivation">
409
+ <strong>why?</strong> safe agentic orchestration needs an isolated runtime —
410
+ traditionally that means a Docker container per user. thebird replaces that with the browser:
411
+ <strong>POSIX + Node.js in V8</strong>, sandboxed by design, spun up in milliseconds,
412
+ accessible anywhere. your api key never leaves localStorage.
413
+ </p>
414
+ <div class="hero-actions">
415
+ <button class="btn-primary" onclick="switchPage('app')">try it live →</button>
416
+ <a href="https://github.com/AnEntrypoint/thebird" class="btn-ghost">source ↗</a>
417
+ <div class="install-strip" id="install-strip">
418
+ <span class="prompt">$</span>
419
+ <span class="cmd">npm install acptoapi</span>
420
+ <button class="copy-btn" onclick="copyInstall()">copy</button>
421
+ </div>
388
422
  </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>
423
+ </section>
424
+
425
+ <!-- serverless callout strip -->
426
+ <div class="serverless-strip">
427
+ <div class="inner">
428
+ <div class="sl-item">
429
+ <em class="sl-icon">⚡</em>
430
+ <div class="sl-label">zero cold start</div>
431
+ <div class="sl-desc">browser loads in ms. no container spin-up. no provisioning.</div>
432
+ </div>
433
+ <div class="sl-item">
434
+ <em class="sl-icon">🔒</em>
435
+ <div class="sl-label">sandboxed by default</div>
436
+ <div class="sl-desc">V8 isolate + browser security model. no root access. no escape.</div>
437
+ </div>
438
+ <div class="sl-item">
439
+ <em class="sl-icon">🌐</em>
440
+ <div class="sl-label">works anywhere</div>
441
+ <div class="sl-desc">any device with a browser. phones, tablets, locked-down corp laptops.</div>
442
+ </div>
443
+ <div class="sl-item">
444
+ <em class="sl-icon">🗄️</em>
445
+ <div class="sl-label">persistent storage</div>
446
+ <div class="sl-desc">IndexedDB filesystem survives reloads. install npm packages. write files.</div>
447
+ </div>
448
+ <div class="sl-item">
449
+ <em class="sl-icon">🔑</em>
450
+ <div class="sl-label">api key stays local</div>
451
+ <div class="sl-desc">stored in localStorage. never sent to any proxy. zero server trust required.</div>
452
+ </div>
404
453
  </div>
454
+ </div>
405
455
 
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>
456
+ <!-- motivation -->
457
+ <section class="motivation-section">
458
+ <div class="section-head">why browser-native?</div>
459
+ <div class="mot-grid">
460
+ <div class="mot-block">
461
+ <h3>the docker problem</h3>
462
+ <p>every agentic AI workflow needs a safe place to run code — read files, execute shell commands, install dependencies. the default answer is Docker: one container per user, per session. that means servers, provisioning, cost, latency, and ops burden. thebird eliminates all of it by moving the sandbox into the browser tab.</p>
463
+ </div>
464
+ <div class="mot-block">
465
+ <h3>browser as runtime</h3>
466
+ <p>V8 already implements a full JavaScript engine. xstate v5 provides a state machine for orchestrating shell execution. IndexedDB gives you a persistent filesystem. a service worker intercepts HTTP to serve files from that filesystem. thebird wires these four primitives together into a real POSIX shell + Node REPL, no native dependencies required.</p>
467
+ </div>
468
+ <div class="mot-block">
469
+ <h3>fast agentic loop</h3>
470
+ <p>when an AI agent wants to run a command, it calls <code>run_command</code>. the shell executes it in V8, returns stdout. no HTTP round trip to a backend, no container spawn, no SSH. latency is measured in microseconds. the agent can iterate 100x faster than any remote sandbox.</p>
471
+ </div>
472
+ <div class="mot-block">
473
+ <h3>acptoapi: any provider</h3>
474
+ <p>the AI routing layer (acptoapi) translates any Anthropic-format message to Gemini, OpenAI, or 20+ providers in-browser. no proxy server needed. the format registry × provider registry design means adding a new provider is one file. circuit breaker, retry, stream guards, capability stripping — all in-browser.</p>
475
+ </div>
413
476
  </div>
477
+ </section>
478
+
479
+ <!-- features grid -->
480
+ <section class="features" id="features">
481
+ <div class="section-head">capabilities</div>
482
+ <div class="feat-grid">
483
+
484
+ <div class="feat-card">
485
+ <em class="glyph">⬡</em>
486
+ <h3>agentic chat</h3>
487
+ <p>tool-calling AI chat running entirely in-browser via acptoapi. tools: read_file, write_file, list_files (idb-backed), run_command, read_terminal, send_to_terminal. no proxy server required. streaming events.</p>
488
+ <span class="feat-tag">in-browser</span>
489
+ <span class="feat-tag">tool-use</span>
490
+ <span class="feat-tag">streaming</span>
491
+ </div>
414
492
 
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>
493
+ <div class="feat-card">
494
+ <em class="glyph">▶</em>
495
+ <h3>posix terminal</h3>
496
+ <p>browser-native shell (xstate v5 state machine, V8 eval) backed by IndexedDB. built-ins: ls, cat, cd, mkdir, rm, cp, mv, echo, env, export, node, npm install, grep, sed, sort, uniq, tr. node repl with persistent scope + require() from idb node_modules.</p>
497
+ <span class="feat-tag">posix</span>
498
+ <span class="feat-tag">node repl</span>
499
+ <span class="feat-tag">idb-fs</span>
500
+ </div>
422
501
 
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>
502
+ <div class="feat-card">
503
+ <em class="glyph">◈</em>
504
+ <h3>live preview</h3>
505
+ <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, no webpack, no vite, no bundler needed.</p>
506
+ <span class="feat-tag">service worker</span>
507
+ <span class="feat-tag">hot reload</span>
508
+ <span class="feat-tag">serverless</span>
509
+ </div>
431
510
 
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>
511
+ <div class="feat-card">
512
+ <em class="glyph">⟐</em>
513
+ <h3>acptoapi bridge</h3>
514
+ <p>translate any Anthropic-format message to Gemini, OpenAI-compat, or any configured provider. format registry × provider registry. xstate streaming actor. flowie transform pipelines. normalized event stream.</p>
515
+ <span class="feat-tag">multi-provider</span>
516
+ <span class="feat-tag">streaming</span>
517
+ <span class="feat-tag">zero-config</span>
518
+ </div>
439
519
 
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>
520
+ <div class="feat-card">
521
+ <em class="glyph">◧</em>
522
+ <h3>idb filesystem</h3>
523
+ <p>persistent IndexedDB-backed virtual filesystem. files survive page reloads. npm packages install and require() from idb node_modules. http.createServer polyfill for running express-style servers entirely in-browser.</p>
524
+ <span class="feat-tag">persistent</span>
525
+ <span class="feat-tag">node compat</span>
526
+ <span class="feat-tag">serverless</span>
527
+ </div>
448
528
 
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>
529
+ <div class="feat-card">
530
+ <em class="glyph">⊞</em>
531
+ <h3>sdk clients</h3>
532
+ <p>drop-in Anthropic and OpenAI SDK clients via acptoapi. <code>new Anthropic({provider,apiKey})</code> backed by any provider. works in-browser and in node. circuit breaker, capability stripping, stream guards included.</p>
533
+ <span class="feat-tag">anthropic sdk</span>
534
+ <span class="feat-tag">openai sdk</span>
535
+ <span class="feat-tag">browser</span>
536
+ </div>
537
+
538
+ <div class="feat-card">
539
+ <em class="glyph">⚙</em>
540
+ <h3>http proxy server</h3>
541
+ <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>
542
+ <span class="feat-tag">local proxy</span>
543
+ <span class="feat-tag">observability</span>
544
+ </div>
545
+
546
+ <div class="feat-card">
547
+ <em class="glyph">◎</em>
548
+ <h3>multi-provider routing</h3>
549
+ <p>createRouter() picks provider+model per request based on taskType and token count. circuit breaker per provider, capability registry, retry with backoff. gemini, openai-compat, 20+ providers. failover automatic.</p>
550
+ <span class="feat-tag">routing</span>
551
+ <span class="feat-tag">circuit breaker</span>
552
+ <span class="feat-tag">retry</span>
553
+ </div>
456
554
 
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
555
  </div>
556
+ </section>
465
557
 
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>
497
- </div>
558
+ <!-- receipt + architecture -->
559
+ <section class="receipt-section">
560
+ <div class="kv-panel">
561
+ <div class="panel-head"><span>project receipt</span><span style="color:var(--green)">● live</span></div>
562
+ <table>
563
+ <tr><td>status</td><td>live · ships continuously</td></tr>
564
+ <tr><td>license</td><td>MIT</td></tr>
565
+ <tr><td>runtime</td><td>browser (V8) + node ≥18</td></tr>
566
+ <tr><td>server required</td><td><strong style="color:var(--green)">none</strong> 100% serverless</td></tr>
567
+ <tr><td>deps</td><td>acptoapi, @google/genai, xstate, flowie</td></tr>
568
+ <tr><td>frontend</td><td>no framework · vanilla js + webjsx</td></tr>
569
+ <tr><td>storage</td><td>IndexedDB (no server, no cloud)</td></tr>
570
+ <tr><td>api key</td><td>localStorage only · never leaves browser</td></tr>
571
+ <tr><td>providers</td>
572
+ <td>
573
+ <div class="providers">
574
+ <span class="chip green">gemini</span>
575
+ <span class="chip">openai</span>
576
+ <span class="chip">together</span>
577
+ <span class="chip">fireworks</span>
578
+ <span class="chip">perplexity</span>
579
+ <span class="chip">groq</span>
580
+ <span class="chip">ollama</span>
581
+ <span class="chip">openrouter</span>
582
+ <span class="chip">20+ more</span>
583
+ </div>
584
+ </td>
585
+ </tr>
586
+ </table>
587
+ </div>
498
588
 
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
589
+ <div>
590
+ <div class="kv-panel" style="margin-bottom:24px">
591
+ <div class="panel-head"><span>architecture</span></div>
592
+ <pre class="arch" style="border:0;margin:0"><span class="hl">thebird</span> (browser web os)
593
+ ├── docs/ browser UI (GH Pages)
594
+ │ ├── chat agentic AI · tool calling
595
+ │ ├── terminal posix shell · node repl · idb-fs
506
596
  │ └── preview service worker · hot reload
507
- ├── serve.js static file server
597
+ ├── serve.js local static file server
508
598
  └── server.js anthropic-compat proxy
509
599
 
510
- <span class="hl">acptoapi</span> (npm dep)
600
+ <span class="hl">acptoapi</span> (npm — runs in browser + node)
511
601
  ├── lib/formats/ anthropic · openai · gemini · acp
512
602
  ├── 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>
603
+ ├── lib/machine.js xstate v5 streaming actor
604
+ ├── lib/translate any format → any provider
605
+ └── lib/sdk/ Anthropic + OpenAI SDK clients
606
+
607
+ <span class="hl">browser sandbox</span> (replaces Docker)
608
+ ├── V8 javascript execution engine
609
+ ├── xstate v5 shell state machine orchestrator
610
+ ├── IndexedDB persistent virtual filesystem
611
+ └── ServiceWorker HTTP server from IDB files</pre>
612
+ </div>
517
613
 
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>
614
+ <div class="kv-panel">
615
+ <div class="panel-head"><span>quick start</span></div>
616
+ <table>
617
+ <tr><td>open in browser</td><td><a href="https://anentrypoint.github.io/thebird/" style="color:var(--green)">anentrypoint.github.io/thebird ↗</a></td></tr>
618
+ <tr><td>local dev</td><td><code>node serve.js</code> → localhost:8080</td></tr>
619
+ <tr><td>proxy server</td><td><code>node server.js</code> → localhost:3456</td></tr>
620
+ <tr><td>acptoapi sdk</td><td><code>npm install acptoapi</code></td></tr>
621
+ <tr><td>source</td><td><a href="https://github.com/AnEntrypoint/thebird" style="color:var(--green)">github.com/AnEntrypoint/thebird ↗</a></td></tr>
622
+ </table>
623
+ </div>
526
624
  </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
-
625
+ </section>
626
+
627
+ <!-- footer -->
628
+ <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);margin-top:auto">
629
+ <span>247420 / anentrypoint</span>
630
+ <span style="color:var(--ink-hair)">·</span>
631
+ <span>thebird</span>
632
+ <span style="color:var(--ink-hair)">·</span>
633
+ <a href="https://github.com/AnEntrypoint/thebird" style="color:var(--ink-dim);text-decoration:none">source ↗</a>
634
+ <span style="color:var(--ink-hair)">·</span>
635
+ <a href="https://github.com/AnEntrypoint/acptoapi" style="color:var(--ink-dim);text-decoration:none">acptoapi ↗</a>
636
+ <span style="margin-left:auto;color:var(--green)">probably emerging 🌀</span>
637
+ </footer>
638
+
639
+ </div><!-- /page-overview -->
640
+
641
+ <!-- LIVE APP PAGE -->
642
+ <div class="page-panel" id="page-app">
538
643
  <div class="app-embed">
539
644
  <div class="tui-tabs">
540
645
  <button id="tab-chat" class="tui-tab active" onclick="switchTab('chat')">chat</button>
541
646
  <button id="tab-term" class="tui-tab" onclick="switchTab('term')">terminal</button>
542
647
  <button id="tab-preview" class="tui-tab" onclick="switchTab('preview')">preview</button>
648
+ <span style="margin-left:auto;padding:0 2ch;font-size:10px;color:var(--ink-dim);line-height:36px">api key in localStorage · no server · <a href="https://github.com/AnEntrypoint/thebird" style="color:var(--ink-dim)">source ↗</a></span>
543
649
  </div>
544
650
  <div id="pane-chat" style="flex:1;overflow:hidden;display:flex;flex-direction:column">
545
651
  <bird-chat></bird-chat>
@@ -554,23 +660,27 @@
554
660
  <iframe id="preview-frame" style="width:100%;flex:1;border:0;background:var(--paper)" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
555
661
  </div>
556
662
  </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>
663
+ </div><!-- /page-app -->
570
664
 
571
665
  </div><!-- /land -->
572
666
 
573
667
  <script>
668
+ function switchPage(p) {
669
+ ['overview', 'app'].forEach(id => {
670
+ document.getElementById('page-' + id).classList.toggle('active', id === p);
671
+ document.getElementById('ptab-' + id).classList.toggle('active', id === p);
672
+ });
673
+ if (p === 'app') {
674
+ document.getElementById('page-app').style.height = (window.innerHeight - 40) + 'px';
675
+ }
676
+ }
677
+
678
+ window.addEventListener('resize', () => {
679
+ if (document.getElementById('page-app').classList.contains('active')) {
680
+ document.getElementById('page-app').style.height = (window.innerHeight - 40) + 'px';
681
+ }
682
+ });
683
+
574
684
  function copyInstall() {
575
685
  navigator.clipboard?.writeText('npm install acptoapi');
576
686
  const btn = document.querySelector('#install-strip .copy-btn');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.106",
3
+ "version": "1.2.108",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
5
  "scripts": {
6
6
  "start": "node serve.js"