astrabot 0.1.0

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.
Files changed (47) hide show
  1. package/README.md +411 -0
  2. package/ai/ai.config.ts +27 -0
  3. package/ai/auto-retry.ts +117 -0
  4. package/ai/config-loader.ts +132 -0
  5. package/ai/index.ts +4 -0
  6. package/ai/retry-prompt.ts +30 -0
  7. package/bin/astra +2 -0
  8. package/core/retry/error-classifier.ts +208 -0
  9. package/core/retry/index.ts +29 -0
  10. package/core/retry/retry-config.ts +142 -0
  11. package/core/retry/retry-engine.ts +215 -0
  12. package/game/index.html +573 -0
  13. package/game/neon-breaker.html +1037 -0
  14. package/index.ts +140 -0
  15. package/modes/agent/action-tracker.ts +47 -0
  16. package/modes/agent/agent-tools.ts +338 -0
  17. package/modes/agent/approval.ts +184 -0
  18. package/modes/agent/diff-view.ts +34 -0
  19. package/modes/agent/orchestrator.ts +234 -0
  20. package/modes/agent/tool-executor.ts +993 -0
  21. package/modes/agent/types.ts +68 -0
  22. package/modes/ask/orchestrator.ts +230 -0
  23. package/modes/auto.ts +88 -0
  24. package/modes/cli.ts +43 -0
  25. package/modes/multi/agent-pool-manager.ts +337 -0
  26. package/modes/multi/examples.ts +441 -0
  27. package/modes/multi/message-broker.ts +179 -0
  28. package/modes/multi/multi-agent-orchestrator.ts +891 -0
  29. package/modes/multi/orchestrator.ts +414 -0
  30. package/modes/multi/types.ts +245 -0
  31. package/modes/multi/workflow-builder.ts +569 -0
  32. package/modes/plan/orchestrator.ts +198 -0
  33. package/modes/plan/planner.ts +121 -0
  34. package/modes/plan/selection.ts +43 -0
  35. package/modes/plan/types.ts +13 -0
  36. package/modes/plan/web-tools.ts +132 -0
  37. package/modes/setup.ts +210 -0
  38. package/package.json +62 -0
  39. package/session/index.ts +45 -0
  40. package/session/session-context.ts +188 -0
  41. package/session/session-manager.ts +374 -0
  42. package/session/session-tools.ts +109 -0
  43. package/session/store.ts +278 -0
  44. package/tsconfig.json +30 -0
  45. package/tui/spinner.ts +182 -0
  46. package/tui/terminal-md.ts +17 -0
  47. package/tui/wakeup.ts +231 -0
@@ -0,0 +1,573 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Snake Game</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ display: flex;
16
+ justify-content: center;
17
+ align-items: center;
18
+ min-height: 100vh;
19
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
20
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
21
+ color: #fff;
22
+ }
23
+
24
+ .game-container {
25
+ text-align: center;
26
+ background: rgba(255, 255, 255, 0.05);
27
+ padding: 30px;
28
+ border-radius: 20px;
29
+ backdrop-filter: blur(10px);
30
+ border: 1px solid rgba(255, 255, 255, 0.1);
31
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
32
+ }
33
+
34
+ h1 {
35
+ margin-bottom: 10px;
36
+ font-size: 2.5rem;
37
+ text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
38
+ color: #00ff88;
39
+ }
40
+
41
+ .score-board {
42
+ display: flex;
43
+ justify-content: center;
44
+ gap: 40px;
45
+ margin-bottom: 20px;
46
+ font-size: 1.2rem;
47
+ }
48
+
49
+ .score, .high-score {
50
+ background: rgba(0, 0, 0, 0.3);
51
+ padding: 10px 20px;
52
+ border-radius: 10px;
53
+ }
54
+
55
+ .score span, .high-score span {
56
+ color: #00ff88;
57
+ font-weight: bold;
58
+ }
59
+
60
+ #gameCanvas {
61
+ border: 2px solid #00ff88;
62
+ border-radius: 10px;
63
+ background: #0a0a1a;
64
+ box-shadow: 0 0 20px rgba(0, 255, 136, 0.2);
65
+ max-width: 100%;
66
+ height: auto;
67
+ }
68
+
69
+ .controls {
70
+ margin-top: 20px;
71
+ display: flex;
72
+ justify-content: center;
73
+ gap: 15px;
74
+ }
75
+
76
+ button {
77
+ padding: 12px 30px;
78
+ font-size: 1rem;
79
+ border: none;
80
+ border-radius: 25px;
81
+ cursor: pointer;
82
+ transition: all 0.3s ease;
83
+ font-weight: bold;
84
+ }
85
+
86
+ #startBtn {
87
+ background: #00ff88;
88
+ color: #1a1a2e;
89
+ }
90
+
91
+ #startBtn:hover {
92
+ background: #00cc6a;
93
+ transform: scale(1.05);
94
+ }
95
+
96
+ #pauseBtn {
97
+ background: transparent;
98
+ border: 2px solid #00ff88;
99
+ color: #00ff88;
100
+ }
101
+
102
+ #pauseBtn:hover {
103
+ background: rgba(0, 255, 136, 0.1);
104
+ }
105
+
106
+ .instructions {
107
+ margin-top: 20px;
108
+ font-size: 0.9rem;
109
+ color: rgba(255, 255, 255, 0.7);
110
+ }
111
+
112
+ .instructions kbd {
113
+ background: rgba(255, 255, 255, 0.2);
114
+ padding: 2px 8px;
115
+ border-radius: 5px;
116
+ margin: 0 3px;
117
+ }
118
+
119
+ .mobile-controls {
120
+ display: none;
121
+ margin-top: 20px;
122
+ gap: 10px;
123
+ }
124
+
125
+ .mobile-controls button {
126
+ width: 60px;
127
+ height: 60px;
128
+ font-size: 1.5rem;
129
+ border-radius: 50%;
130
+ background: rgba(0, 255, 136, 0.2);
131
+ border: 2px solid #00ff88;
132
+ color: #00ff88;
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ }
137
+
138
+ .mobile-controls button:active {
139
+ background: rgba(0, 255, 136, 0.4);
140
+ }
141
+
142
+ .arrow-grid {
143
+ display: grid;
144
+ grid-template-columns: repeat(3, 60px);
145
+ grid-template-rows: repeat(2, 60px);
146
+ gap: 5px;
147
+ justify-content: center;
148
+ }
149
+
150
+ .arrow-grid button:nth-child(1) { grid-column: 2; }
151
+ .arrow-grid button:nth-child(2) { grid-column: 1; grid-row: 2; }
152
+ .arrow-grid button:nth-child(3) { grid-column: 2; grid-row: 2; }
153
+ .arrow-grid button:nth-child(4) { grid-column: 3; grid-row: 2; }
154
+
155
+ @media (max-width: 600px) {
156
+ .mobile-controls {
157
+ display: block;
158
+ }
159
+ .instructions {
160
+ display: none;
161
+ }
162
+ h1 {
163
+ font-size: 1.8rem;
164
+ }
165
+ .score-board {
166
+ flex-direction: column;
167
+ gap: 10px;
168
+ }
169
+ }
170
+
171
+ .game-over {
172
+ position: fixed;
173
+ top: 0;
174
+ left: 0;
175
+ width: 100%;
176
+ height: 100%;
177
+ background: rgba(0, 0, 0, 0.8);
178
+ display: none;
179
+ justify-content: center;
180
+ align-items: center;
181
+ z-index: 100;
182
+ }
183
+
184
+ .game-over-content {
185
+ background: linear-gradient(135deg, #1a1a2e, #16213e);
186
+ padding: 40px;
187
+ border-radius: 20px;
188
+ text-align: center;
189
+ border: 2px solid #ff4757;
190
+ box-shadow: 0 0 30px rgba(255, 71, 87, 0.3);
191
+ }
192
+
193
+ .game-over-content h2 {
194
+ color: #ff4757;
195
+ font-size: 2.5rem;
196
+ margin-bottom: 20px;
197
+ }
198
+
199
+ .game-over-content p {
200
+ font-size: 1.3rem;
201
+ margin-bottom: 20px;
202
+ }
203
+
204
+ .game-over-content button {
205
+ background: #ff4757;
206
+ color: white;
207
+ }
208
+
209
+ .game-over-content button:hover {
210
+ background: #cc3a47;
211
+ transform: scale(1.05);
212
+ }
213
+ </style>
214
+ </head>
215
+ <body>
216
+ <div class="game-container">
217
+ <h1>🐍 Snake Game</h1>
218
+ <div class="score-board">
219
+ <div class="score">Score: <span id="score">0</span></div>
220
+ <div class="high-score">Best: <span id="highScore">0</span></div>
221
+ </div>
222
+ <canvas id="gameCanvas" width="400" height="400"></canvas>
223
+ <div class="controls">
224
+ <button id="startBtn">Start Game</button>
225
+ <button id="pauseBtn">Pause</button>
226
+ </div>
227
+ <div class="mobile-controls">
228
+ <div class="arrow-grid">
229
+ <button id="upBtn">↑</button>
230
+ <button id="leftBtn">←</button>
231
+ <button id="downBtn">↓</button>
232
+ <button id="rightBtn">→</button>
233
+ </div>
234
+ </div>
235
+ <p class="instructions">
236
+ Use <kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd> <kbd>→</kbd> or <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> to move
237
+ </p>
238
+ </div>
239
+
240
+ <div class="game-over" id="gameOverScreen">
241
+ <div class="game-over-content">
242
+ <h2>Game Over!</h2>
243
+ <p>Your score: <span id="finalScore">0</span></p>
244
+ <button id="restartBtn">Play Again</button>
245
+ </div>
246
+ </div>
247
+
248
+ <script>
249
+ const canvas = document.getElementById('gameCanvas');
250
+ const ctx = canvas.getContext('2d');
251
+
252
+ // High DPI support
253
+ const dpr = window.devicePixelRatio || 1;
254
+ canvas.width = 400 * dpr;
255
+ canvas.height = 400 * dpr;
256
+ ctx.scale(dpr, dpr);
257
+
258
+ const gridSize = 20;
259
+ const tileCount = 400 / gridSize;
260
+
261
+ let snake = [];
262
+ let food = {};
263
+ let direction = { x: 1, y: 0 };
264
+ let inputQueue = [];
265
+ let score = 0;
266
+ let highScore = 0;
267
+
268
+ try {
269
+ highScore = localStorage.getItem('snakeHighScore') || 0;
270
+ } catch (e) {
271
+ console.warn('LocalStorage not available');
272
+ }
273
+
274
+ let gameLoop = null;
275
+ let gameSpeed = 100;
276
+ let isPaused = false;
277
+ let isGameOver = false;
278
+ let isGameRunning = false;
279
+
280
+ document.getElementById('highScore').textContent = highScore;
281
+
282
+ function initGame() {
283
+ snake = [
284
+ { x: 5, y: 10 },
285
+ { x: 4, y: 10 },
286
+ { x: 3, y: 10 }
287
+ ];
288
+ direction = { x: 1, y: 0 };
289
+ inputQueue = [];
290
+ score = 0;
291
+ gameSpeed = 100;
292
+ isPaused = false;
293
+ isGameOver = false;
294
+ document.getElementById('score').textContent = score;
295
+ spawnFood();
296
+ }
297
+
298
+ function spawnFood() {
299
+ let validPosition = false;
300
+ let attempts = 0;
301
+ const maxAttempts = 100;
302
+
303
+ while (!validPosition && attempts < maxAttempts) {
304
+ food = {
305
+ x: Math.floor(Math.random() * tileCount),
306
+ y: Math.floor(Math.random() * tileCount)
307
+ };
308
+
309
+ validPosition = !snake.some(segment => segment.x === food.x && segment.y === food.y);
310
+ attempts++;
311
+ }
312
+
313
+ if (!validPosition) {
314
+ const emptyTiles = [];
315
+ for (let x = 0; x < tileCount; x++) {
316
+ for (let y = 0; y < tileCount; y++) {
317
+ if (!snake.some(s => s.x === x && s.y === y)) {
318
+ emptyTiles.push({x, y});
319
+ }
320
+ }
321
+ }
322
+
323
+ if (emptyTiles.length > 0) {
324
+ food = emptyTiles[Math.floor(Math.random() * emptyTiles.length)];
325
+ } else {
326
+ gameOver(true);
327
+ }
328
+ }
329
+ }
330
+
331
+ function draw() {
332
+ ctx.fillStyle = '#0a0a1a';
333
+ ctx.fillRect(0, 0, 400, 400);
334
+
335
+ ctx.strokeStyle = 'rgba(0, 255, 136, 0.05)';
336
+ ctx.lineWidth = 1;
337
+ for (let i = 0; i <= tileCount; i++) {
338
+ ctx.beginPath();
339
+ ctx.moveTo(i * gridSize, 0);
340
+ ctx.lineTo(i * gridSize, 400);
341
+ ctx.stroke();
342
+ ctx.beginPath();
343
+ ctx.moveTo(0, i * gridSize);
344
+ ctx.lineTo(400, i * gridSize);
345
+ ctx.stroke();
346
+ }
347
+
348
+ ctx.shadowBlur = 15;
349
+ ctx.shadowColor = '#ff6b6b';
350
+ ctx.fillStyle = '#ff6b6b';
351
+ ctx.beginPath();
352
+ ctx.arc(
353
+ food.x * gridSize + gridSize / 2,
354
+ food.y * gridSize + gridSize / 2,
355
+ gridSize / 2 - 2,
356
+ 0,
357
+ Math.PI * 2
358
+ );
359
+ ctx.fill();
360
+ ctx.shadowBlur = 0;
361
+
362
+ snake.forEach((segment, index) => {
363
+ const gradient = ctx.createRadialGradient(
364
+ segment.x * gridSize + gridSize / 2,
365
+ segment.y * gridSize + gridSize / 2,
366
+ 0,
367
+ segment.x * gridSize + gridSize / 2,
368
+ segment.y * gridSize + gridSize / 2,
369
+ gridSize / 2
370
+ );
371
+
372
+ if (index === 0) {
373
+ gradient.addColorStop(0, '#00ff88');
374
+ gradient.addColorStop(1, '#00cc6a');
375
+ ctx.shadowBlur = 10;
376
+ ctx.shadowColor = '#00ff88';
377
+ } else {
378
+ const alpha = 1 - (index / snake.length) * 0.5;
379
+ gradient.addColorStop(0, `rgba(0, 255, 136, ${alpha})`);
380
+ gradient.addColorStop(1, `rgba(0, 204, 106, ${alpha})`);
381
+ ctx.shadowBlur = 0;
382
+ }
383
+
384
+ ctx.fillStyle = gradient;
385
+ ctx.fillRect(
386
+ segment.x * gridSize + 1,
387
+ segment.y * gridSize + 1,
388
+ gridSize - 2,
389
+ gridSize - 2
390
+ );
391
+
392
+ if (index === 0) {
393
+ ctx.shadowBlur = 0;
394
+ ctx.fillStyle = '#0a0a1a';
395
+ const eyeSize = 3;
396
+ const eyeOffset = 5;
397
+
398
+ let eye1X, eye1Y, eye2X, eye2Y;
399
+
400
+ if (direction.x === 1) {
401
+ eye1X = segment.x * gridSize + gridSize - eyeOffset;
402
+ eye1Y = segment.y * gridSize + eyeOffset;
403
+ eye2X = segment.x * gridSize + gridSize - eyeOffset;
404
+ eye2Y = segment.y * gridSize + gridSize - eyeOffset;
405
+ } else if (direction.x === -1) {
406
+ eye1X = segment.x * gridSize + eyeOffset;
407
+ eye1Y = segment.y * gridSize + eyeOffset;
408
+ eye2X = segment.x * gridSize + eyeOffset;
409
+ eye2Y = segment.y * gridSize + gridSize - eyeOffset;
410
+ } else if (direction.y === -1) {
411
+ eye1X = segment.x * gridSize + eyeOffset;
412
+ eye1Y = segment.y * gridSize + eyeOffset;
413
+ eye2X = segment.x * gridSize + gridSize - eyeOffset;
414
+ eye2Y = segment.y * gridSize + eyeOffset;
415
+ } else {
416
+ eye1X = segment.x * gridSize + eyeOffset;
417
+ eye1Y = segment.y * gridSize + gridSize - eyeOffset;
418
+ eye2X = segment.x * gridSize + gridSize - eyeOffset;
419
+ eye2Y = segment.y * gridSize + gridSize - eyeOffset;
420
+ }
421
+
422
+ ctx.beginPath();
423
+ ctx.arc(eye1X, eye1Y, eyeSize, 0, Math.PI * 2);
424
+ ctx.arc(eye2X, eye2Y, eyeSize, 0, Math.PI * 2);
425
+ ctx.fill();
426
+ }
427
+ });
428
+ ctx.shadowBlur = 0;
429
+ }
430
+
431
+ function update() {
432
+ if (inputQueue.length > 0) {
433
+ direction = inputQueue.shift();
434
+ }
435
+
436
+ const head = {
437
+ x: snake[0].x + direction.x,
438
+ y: snake[0].y + direction.y
439
+ };
440
+
441
+ if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
442
+ gameOver();
443
+ return;
444
+ }
445
+
446
+ if (snake.some(segment => segment.x === head.x && segment.y === head.y)) {
447
+ gameOver();
448
+ return;
449
+ }
450
+
451
+ snake.unshift(head);
452
+
453
+ if (head.x === food.x && head.y === food.y) {
454
+ score += 10;
455
+ document.getElementById('score').textContent = score;
456
+ spawnFood();
457
+
458
+ if (gameSpeed > 50) {
459
+ gameSpeed -= 2;
460
+ clearInterval(gameLoop);
461
+ gameLoop = setInterval(gameStep, gameSpeed);
462
+ }
463
+ } else {
464
+ snake.pop();
465
+ }
466
+ }
467
+
468
+ function gameStep() {
469
+ if (!isPaused && !isGameOver) {
470
+ update();
471
+ draw();
472
+ }
473
+ }
474
+
475
+ function gameOver(win = false) {
476
+ isGameOver = true;
477
+ clearInterval(gameLoop);
478
+
479
+ if (score > highScore) {
480
+ highScore = score;
481
+ try {
482
+ localStorage.setItem('snakeHighScore', highScore);
483
+ } catch(e) {}
484
+ document.getElementById('highScore').textContent = highScore;
485
+ }
486
+
487
+ document.getElementById('finalScore').textContent = score;
488
+ const gameOverScreen = document.getElementById('gameOverScreen');
489
+ const title = gameOverScreen.querySelector('h2');
490
+ title.textContent = win ? "You Win!" : "Game Over!";
491
+ title.style.color = win ? "#00ff88" : "#ff4757";
492
+ gameOverScreen.style.display = 'flex';
493
+ }
494
+
495
+ function startGame() {
496
+ if (gameLoop) clearInterval(gameLoop);
497
+ document.getElementById('gameOverScreen').style.display = 'none';
498
+ initGame();
499
+ isGameRunning = true;
500
+ draw();
501
+ gameLoop = setInterval(gameStep, gameSpeed);
502
+ }
503
+
504
+ function togglePause() {
505
+ if (!isGameRunning) return;
506
+ isPaused = !isPaused;
507
+ document.getElementById('pauseBtn').textContent = isPaused ? 'Resume' : 'Pause';
508
+ }
509
+
510
+ function handleInput(newDir) {
511
+ if (!isGameRunning || isPaused) return;
512
+
513
+ const lastScheduledDir = inputQueue.length > 0
514
+ ? inputQueue[inputQueue.length - 1]
515
+ : direction;
516
+
517
+ const opposites = {
518
+ 'x': { '1': '-1', '-1': '1', '0': '0' },
519
+ 'y': { '1': '-1', '-1': '1', '0': '0' }
520
+ };
521
+
522
+ const isOpposite = (newDir.x !== 0 && opposites.x[String(newDir.x)] === String(lastScheduledDir.x)) ||
523
+ (newDir.y !== 0 && opposites.y[String(newDir.y)] === String(lastScheduledDir.y));
524
+
525
+ if ((newDir.x !== 0 && lastScheduledDir.x === 0) ||
526
+ (newDir.y !== 0 && lastScheduledDir.y === 0)) {
527
+ if (inputQueue.length < 2) {
528
+ inputQueue.push(newDir);
529
+ }
530
+ }
531
+ }
532
+
533
+ document.addEventListener('keydown', (e) => {
534
+ const keyMap = {
535
+ 'ArrowUp': { x: 0, y: -1 },
536
+ 'ArrowDown': { x: 0, y: 1 },
537
+ 'ArrowLeft': { x: -1, y: 0 },
538
+ 'ArrowRight': { x: 1, y: 0 },
539
+ 'w': { x: 0, y: -1 },
540
+ 's': { x: 0, y: 1 },
541
+ 'a': { x: -1, y: 0 },
542
+ 'd': { x: 1, y: 0 },
543
+ 'W': { x: 0, y: -1 },
544
+ 'S': { x: 0, y: 1 },
545
+ 'A': { x: -1, y: 0 },
546
+ 'D': { x: 1, y: 0 }
547
+ };
548
+
549
+ if (keyMap[e.key]) {
550
+ e.preventDefault();
551
+ handleInput(keyMap[e.key]);
552
+ }
553
+
554
+ if (e.key === ' ' && isGameRunning) {
555
+ e.preventDefault();
556
+ togglePause();
557
+ }
558
+ });
559
+
560
+ document.getElementById('startBtn').addEventListener('click', startGame);
561
+ document.getElementById('pauseBtn').addEventListener('click', togglePause);
562
+ document.getElementById('restartBtn').addEventListener('click', startGame);
563
+
564
+ document.getElementById('upBtn').addEventListener('click', () => handleInput({ x: 0, y: -1 }));
565
+ document.getElementById('downBtn').addEventListener('click', () => handleInput({ x: 0, y: 1 }));
566
+ document.getElementById('leftBtn').addEventListener('click', () => handleInput({ x: -1, y: 0 }));
567
+ document.getElementById('rightBtn').addEventListener('click', () => handleInput({ x: 1, y: 0 }));
568
+
569
+ initGame();
570
+ draw();
571
+ </script>
572
+ </body>
573
+ </html>