3v324v23 commited on
Commit
bbc9709
·
1 Parent(s): 62ab417

Fix permissions issues on HuggingFace Space: Add fallback UI options

Browse files
Files changed (4) hide show
  1. Dockerfile +4 -8
  2. app.py +298 -35
  3. simple_next_app.js +34 -0
  4. start.sh +8 -2
Dockerfile CHANGED
@@ -18,8 +18,8 @@ COPY proxy.py /app/
18
  COPY .space/hf-space.sh /app/
19
 
20
  # Создаем директории
21
- RUN mkdir -p /tmp/ten_user/agents /tmp/ten_user/logs /app/backup
22
- RUN chmod -R 777 /tmp /app/backup
23
 
24
  # Создаем и активируем виртуальную среду Python
25
  RUN python3 -m venv /app/venv
@@ -38,12 +38,8 @@ RUN mkdir -p /app/playground
38
  RUN cp -r /tmp/ten-agent/playground/* /app/playground/
39
  RUN rm -rf /tmp/ten-agent
40
 
41
- # Устанавливаем pnpm
42
- RUN npm install -g pnpm@8
43
-
44
- # Устанавливаем dependencies для playground
45
- WORKDIR /app/playground
46
- RUN pnpm install
47
 
48
  # Возвращаемся в основную директорию
49
  WORKDIR /app
 
18
  COPY .space/hf-space.sh /app/
19
 
20
  # Создаем директории
21
+ RUN mkdir -p /tmp/ten_user/agents /tmp/ten_user/logs /app/backup /tmp/ten_playground
22
+ RUN chmod -R 777 /tmp/ten_playground
23
 
24
  # Создаем и активируем виртуальную среду Python
25
  RUN python3 -m venv /app/venv
 
38
  RUN cp -r /tmp/ten-agent/playground/* /app/playground/
39
  RUN rm -rf /tmp/ten-agent
40
 
41
+ # Устанавливаем pnpm и next глобально для использования с npx
42
+ RUN npm install -g pnpm@8 next@latest
 
 
 
 
43
 
44
  # Возвращаемся в основную директорию
45
  WORKDIR /app
app.py CHANGED
@@ -240,6 +240,31 @@ def start_playground():
240
  """Запускает Playground UI через Next.js"""
241
  print("Запуск Playground UI...")
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  # Устанавливаем переменные окружения
244
  ui_env = os.environ.copy()
245
  ui_env["PORT"] = str(NEXTJS_PORT)
@@ -254,8 +279,8 @@ def start_playground():
254
  # Отключаем строгие проверки CORS для работы в iframe
255
  ui_env["NEXT_PUBLIC_DISABLE_CORS"] = "true"
256
 
257
- # Запускаем UI
258
- ui_cmd = "cd playground && npm run dev"
259
  print(f"Running UI command: {ui_cmd}")
260
  ui_process = subprocess.Popen(
261
  ui_cmd,
@@ -293,7 +318,7 @@ def create_interface():
293
  """Создает Gradio интерфейс для редиректа"""
294
  with gr.Blocks() as demo:
295
  gr.Markdown("# TEN Agent на Hugging Face Space")
296
- gr.Markdown("## Загрузка приложения...")
297
 
298
  # Статус серверов
299
  status_md = gr.Markdown("### Статус: Инициализация...")
@@ -302,40 +327,76 @@ def create_interface():
302
  col1, col2 = gr.Column(), gr.Column()
303
 
304
  with col1:
305
- # Кнопка для открытия UI в iframe
306
- open_iframe = gr.Button("Показать TEN Agent в iframe")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
  # Функция для открытия UI в iframe
 
 
309
  def show_iframe():
310
  return f"""
311
  <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
312
- <iframe src="{UI_URL}" width="100%" height="600px" frameborder="0"></iframe>
313
  </div>
314
  """
315
 
316
  iframe_area = gr.HTML()
317
- open_iframe.click(show_iframe, outputs=iframe_area)
 
 
 
 
 
318
 
319
- with col2:
320
- # Ссылка на UI
321
- gr.Markdown(f"""
322
- ### Открыть TEN Agent в новой вкладке:
323
-
324
- <a href="{UI_URL}" target="_blank" style="display: inline-block; padding: 10px 15px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 10px 0;">Открыть TEN Agent UI</a>
325
-
326
- ### ВАЖНО:
327
-
328
- Настройте API ключи в интерфейсе для полноценной работы:
329
- - OpenAI API Key
330
- - ElevenLabs API Key
331
- - Deepgram API Key
332
- - Agora App ID и Certificate
333
- """)
334
 
335
  # Статус серверов и логи
336
- with gr.Accordion("Системная информация", open=False):
337
- api_status = gr.Textbox(label="Статус API сервера", value="Запускается...", interactive=False)
338
- ui_status = gr.Textbox(label="Статус UI сервера", value="Запускается...", interactive=False)
339
 
340
  # Функция обновления статуса
341
  def update_status():
@@ -346,6 +407,9 @@ def create_interface():
346
 
347
  status_btn = gr.Button("Обновить статус")
348
  status_btn.click(update_status, outputs=[api_status, ui_status, status_md])
 
 
 
349
 
350
  return demo
351
 
@@ -359,6 +423,199 @@ def is_port_in_use(port):
359
  except:
360
  return False
361
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  def main():
363
  # Создаем директории и файлы конфигурации
364
  create_directories()
@@ -370,12 +627,16 @@ def main():
370
  print("Не удалось запустить API сервер")
371
  return
372
 
373
- # Запускаем Playground UI
374
  ui_process = start_playground()
 
 
375
  if not ui_process:
376
- print("Не удалось запустить Playground UI")
377
- api_process.terminate()
378
- return
 
 
379
 
380
  # Создаем Gradio интерфейс
381
  demo = create_interface()
@@ -388,19 +649,21 @@ def main():
388
  while True:
389
  if api_process.poll() is not None:
390
  print("API сервер остановлен")
391
- ui_process.terminate()
 
392
  break
393
 
394
- if ui_process.poll() is not None:
395
- print("UI остановлен")
396
- api_process.terminate()
397
- break
398
 
399
  time.sleep(1)
400
  except KeyboardInterrupt:
401
  print("Принудительная остановка...")
402
  api_process.terminate()
403
- ui_process.terminate()
 
404
 
405
  if __name__ == "__main__":
406
  # Корректная обработка сигналов
 
240
  """Запускает Playground UI через Next.js"""
241
  print("Запуск Playground UI...")
242
 
243
+ # Создаем директорию для Next.js в /tmp, где у нас есть права на запись
244
+ tmp_playground_dir = Path("/tmp/ten_playground")
245
+ if not tmp_playground_dir.exists():
246
+ print(f"Создаем временную директорию для Next.js: {tmp_playground_dir}")
247
+ tmp_playground_dir.mkdir(exist_ok=True, parents=True)
248
+
249
+ # Копируем необходимые файлы из /app/playground в /tmp/ten_playground
250
+ print("Копируем файлы Next.js приложения во временную директорию...")
251
+ try:
252
+ os.system(f"cp -r /app/playground/app /tmp/ten_playground/")
253
+ os.system(f"cp -r /app/playground/public /tmp/ten_playground/")
254
+ os.system(f"cp /app/playground/package.json /tmp/ten_playground/")
255
+ os.system(f"cp /app/playground/next.config.mjs /tmp/ten_playground/")
256
+ os.system(f"cp /app/playground/tailwind.config.js /tmp/ten_playground/")
257
+ os.system(f"cp /app/playground/postcss.config.js /tmp/ten_playground/")
258
+
259
+ # Проверяем, что файлы скопировались
260
+ if not (tmp_playground_dir / "app").exists():
261
+ print("Не удалось скопировать файлы Next.js. Создаем простое приложение...")
262
+ create_simple_next_app(tmp_playground_dir)
263
+ except Exception as e:
264
+ print(f"Ошибка при копировании файлов: {e}")
265
+ print("Создаем простое приложение Next.js...")
266
+ create_simple_next_app(tmp_playground_dir)
267
+
268
  # Устанавливаем переменные окружения
269
  ui_env = os.environ.copy()
270
  ui_env["PORT"] = str(NEXTJS_PORT)
 
279
  # Отключаем строгие проверки CORS для работы в iframe
280
  ui_env["NEXT_PUBLIC_DISABLE_CORS"] = "true"
281
 
282
+ # Запускаем UI из временной директории, где у нас есть права на запись
283
+ ui_cmd = f"cd {tmp_playground_dir} && npx next dev"
284
  print(f"Running UI command: {ui_cmd}")
285
  ui_process = subprocess.Popen(
286
  ui_cmd,
 
318
  """Создает Gradio интерфейс для редиректа"""
319
  with gr.Blocks() as demo:
320
  gr.Markdown("# TEN Agent на Hugging Face Space")
321
+ gr.Markdown("## Управление и мониторинг")
322
 
323
  # Статус серверов
324
  status_md = gr.Markdown("### Статус: Инициализация...")
 
327
  col1, col2 = gr.Column(), gr.Column()
328
 
329
  with col1:
330
+ # Информация об API сервере
331
+ gr.Markdown(f"""
332
+ ### API сервер
333
+
334
+ API сервер работает по адресу: http://{INTERNAL_HOST}:{API_PORT}
335
+
336
+ Доступные эндпоинты:
337
+ - `/graphs` - Список доступных графов
338
+ - `/health` - Статус API сервера
339
+ """)
340
+
341
+ # Кнопка для проверки API
342
+ check_api_btn = gr.Button("Проверить API сервер")
343
+ api_result = gr.JSON(label="Результат запроса к API")
344
+
345
+ def check_api():
346
+ try:
347
+ import requests
348
+ response = requests.get(f"http://{INTERNAL_HOST}:{API_PORT}/health")
349
+ return response.json()
350
+ except Exception as e:
351
+ return {"status": "error", "message": str(e)}
352
+
353
+ check_api_btn.click(check_api, outputs=api_result)
354
+
355
+ with col2:
356
+ # Информация о UI сервере
357
+ gr.Markdown(f"""
358
+ ### UI сервер
359
+
360
+ UI сервер доступен по адресу: {UI_URL}
361
+
362
+ <a href="{UI_URL}" target="_blank" style="display: inline-block; padding: 10px 15px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 10px 0;">Открыть UI в новой вкладке</a>
363
+ """)
364
 
365
  # Функция для открытия UI в iframe
366
+ iframe_btn = gr.Button("Показать UI в iframe")
367
+
368
  def show_iframe():
369
  return f"""
370
  <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
371
+ <iframe src="{UI_URL}" width="100%" height="500px" frameborder="0"></iframe>
372
  </div>
373
  """
374
 
375
  iframe_area = gr.HTML()
376
+ iframe_btn.click(show_iframe, outputs=iframe_area)
377
+
378
+ # Ссылки на документацию и важные настройки
379
+ with gr.Accordion("Инструкции", open=False):
380
+ gr.Markdown("""
381
+ ### Важные настройки
382
 
383
+ Для полноценной работы необходимо настроить следующие API ключи:
384
+
385
+ 1. **OpenAI API Key** - для работы с языковыми моделями
386
+ 2. **Deepgram API Key** - для распознавания речи
387
+ 3. **ElevenLabs API Key** - для синтеза речи
388
+ 4. **Agora App ID и Certificate** - для работы с RTC
389
+
390
+ ### Доступные графы
391
+
392
+ 1. **Voice Agent** - Голосовой агент с OpenAI и ElevenLabs
393
+ 2. **Chat Agent** - Текстовый чат с OpenAI
394
+ """)
 
 
 
395
 
396
  # Статус серверов и логи
397
+ with gr.Accordion("Статус системы", open=False):
398
+ api_status = gr.Textbox(label="Статус API сервера", value="Проверка...", interactive=False)
399
+ ui_status = gr.Textbox(label="Статус UI сервера", value="Проверка...", interactive=False)
400
 
401
  # Функция обновления статуса
402
  def update_status():
 
407
 
408
  status_btn = gr.Button("Обновить статус")
409
  status_btn.click(update_status, outputs=[api_status, ui_status, status_md])
410
+
411
+ # Обновляем статус при загрузке
412
+ demo.load(update_status, outputs=[api_status, ui_status, status_md])
413
 
414
  return demo
415
 
 
423
  except:
424
  return False
425
 
426
+ def create_simple_next_app(target_dir):
427
+ """Создает простое Next.js приложение в указанной директории"""
428
+ print(f"Создание простого Next.js приложения в {target_dir}")
429
+
430
+ # Создаем базовую структуру Next.js приложения
431
+ app_dir = target_dir / "app"
432
+ app_dir.mkdir(exist_ok=True, parents=True)
433
+
434
+ # Создаем простой package.json
435
+ package_json = {
436
+ "name": "ten-agent-simple-ui",
437
+ "version": "0.1.0",
438
+ "private": True,
439
+ "scripts": {
440
+ "dev": "next dev",
441
+ "build": "next build",
442
+ "start": "next start"
443
+ },
444
+ "dependencies": {
445
+ "next": "latest",
446
+ "react": "latest",
447
+ "react-dom": "latest"
448
+ }
449
+ }
450
+
451
+ with open(target_dir / "package.json", "w") as f:
452
+ json.dump(package_json, f, indent=2)
453
+
454
+ # Создаем простой next.config.js
455
+ next_config = """/** @type {import('next').NextConfig} */
456
+ const nextConfig = {
457
+ reactStrictMode: true,
458
+ }
459
+
460
+ module.exports = nextConfig
461
+ """
462
+
463
+ with open(target_dir / "next.config.js", "w") as f:
464
+ f.write(next_config)
465
+
466
+ # Создаем простую страницу
467
+ page_content = """export default function Home() {
468
+ return (
469
+ <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
470
+ <h1>TEN Agent UI</h1>
471
+ <p>API server is running at: <a href="http://localhost:8080">http://localhost:8080</a></p>
472
+
473
+ <div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0', borderRadius: '5px' }}>
474
+ <p>API endpoints:</p>
475
+ <ul>
476
+ <li><a href="http://localhost:8080/graphs">/graphs</a> - Available graphs</li>
477
+ <li><a href="http://localhost:8080/health">/health</a> - API server status</li>
478
+ </ul>
479
+ </div>
480
+
481
+ <div style={{ marginTop: '20px' }}>
482
+ <button
483
+ style={{
484
+ padding: '10px 15px',
485
+ backgroundColor: '#4CAF50',
486
+ color: 'white',
487
+ border: 'none',
488
+ borderRadius: '5px',
489
+ cursor: 'pointer'
490
+ }}
491
+ onClick={() => window.location.href = 'http://localhost:8080/graphs'}
492
+ >
493
+ Go to API
494
+ </button>
495
+ </div>
496
+ </div>
497
+ );
498
+ }
499
+ """
500
+
501
+ with open(app_dir / "page.js", "w") as f:
502
+ f.write(page_content)
503
+
504
+ # Создаем простой layout.js
505
+ layout_content = """export const metadata = {
506
+ title: 'TEN Agent',
507
+ description: 'Simple UI for TEN Agent',
508
+ }
509
+
510
+ export default function RootLayout({ children }) {
511
+ return (
512
+ <html lang="en">
513
+ <body>{children}</body>
514
+ </html>
515
+ )
516
+ }
517
+ """
518
+
519
+ with open(app_dir / "layout.js", "w") as f:
520
+ f.write(layout_content)
521
+
522
+ print(f"Простое Next.js приложение создано в {target_dir}")
523
+
524
+ def start_simple_ui():
525
+ """Запускает простой HTTP сервер для UI"""
526
+ print("Запуск простого HTTP сервера...")
527
+
528
+ # Создаем директорию для UI
529
+ simple_ui_dir = Path("/tmp/ten_ui")
530
+ simple_ui_dir.mkdir(exist_ok=True, parents=True)
531
+
532
+ # Создаем простую HTML страницу
533
+ html_content = """<!DOCTYPE html>
534
+ <html lang="en">
535
+ <head>
536
+ <meta charset="UTF-8">
537
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
538
+ <title>TEN Agent Simple UI</title>
539
+ <style>
540
+ body {
541
+ font-family: Arial, sans-serif;
542
+ max-width: 800px;
543
+ margin: 0 auto;
544
+ padding: 20px;
545
+ }
546
+ .card {
547
+ background-color: #f5f5f5;
548
+ border-radius: 8px;
549
+ padding: 16px;
550
+ margin-bottom: 16px;
551
+ }
552
+ button {
553
+ background-color: #4CAF50;
554
+ border: none;
555
+ color: white;
556
+ padding: 10px 15px;
557
+ text-align: center;
558
+ text-decoration: none;
559
+ display: inline-block;
560
+ font-size: 16px;
561
+ margin: 4px 2px;
562
+ cursor: pointer;
563
+ border-radius: 4px;
564
+ }
565
+ a {
566
+ color: #0066cc;
567
+ }
568
+ </style>
569
+ </head>
570
+ <body>
571
+ <h1>TEN Agent UI</h1>
572
+ <div class="card">
573
+ <h2>API Server</h2>
574
+ <p>API сервер работает по адресу: <a href="http://localhost:8080" target="_blank">http://localhost:8080</a></p>
575
+ <p>Доступные эндпоинты:</p>
576
+ <ul>
577
+ <li><a href="http://localhost:8080/graphs" target="_blank">/graphs</a> - Список доступных графов</li>
578
+ <li><a href="http://localhost:8080/health" target="_blank">/health</a> - Статус API сервера</li>
579
+ </ul>
580
+ </div>
581
+
582
+ <div class="card">
583
+ <h2>Запуск сессии</h2>
584
+ <p>Для запуска сессии можно использовать API вручную:</p>
585
+ <pre>curl -X POST http://localhost:8080/start -H "Content-Type: application/json" -d '{"graph_file":"voice_agent.json"}'</pre>
586
+ </div>
587
+ </body>
588
+ </html>
589
+ """
590
+
591
+ with open(simple_ui_dir / "index.html", "w") as f:
592
+ f.write(html_content)
593
+
594
+ # Запускаем простой HTTP сервер на порту 3000
595
+ server_cmd = f"cd {simple_ui_dir} && python -m http.server {NEXTJS_PORT}"
596
+ print(f"Running simple HTTP server: {server_cmd}")
597
+
598
+ server_process = subprocess.Popen(
599
+ server_cmd,
600
+ shell=True,
601
+ stdout=subprocess.PIPE,
602
+ stderr=subprocess.PIPE
603
+ )
604
+
605
+ # Ждем запуска сервера
606
+ time.sleep(2)
607
+
608
+ # Проверяем, что процесс не упал
609
+ if server_process.poll() is not None:
610
+ stdout, stderr = server_process.communicate()
611
+ print(f"Простой HTTP сервер не запустился!")
612
+ print(f"STDOUT: {stdout.decode()}")
613
+ print(f"STDERR: {stderr.decode()}")
614
+ return None
615
+
616
+ print(f"Простой HTTP сервер запущен и слушает на порту {NEXTJS_PORT}")
617
+ return server_process
618
+
619
  def main():
620
  # Создаем директории и файлы конфигурации
621
  create_directories()
 
627
  print("Не удалось запустить API сервер")
628
  return
629
 
630
+ # Пробуем запустить Playground UI через Next.js
631
  ui_process = start_playground()
632
+
633
+ # Если запуск через Next.js не удался, пробуем запустить простой HTTP сервер
634
  if not ui_process:
635
+ print("Не удалось запустить Playground UI через Next.js, пробуем простой HTTP сервер...")
636
+ ui_process = start_simple_ui()
637
+
638
+ if not ui_process:
639
+ print("Не удалось запустить ни один UI сервер. Продолжаем только с API сервером.")
640
 
641
  # Создаем Gradio интерфейс
642
  demo = create_interface()
 
649
  while True:
650
  if api_process.poll() is not None:
651
  print("API сервер остановлен")
652
+ if ui_process and ui_process.poll() is None:
653
+ ui_process.terminate()
654
  break
655
 
656
+ if ui_process and ui_process.poll() is not None:
657
+ print("UI сервер остановлен, пробуем перезапустить...")
658
+ # Пробуем запустить простой HTTP сервер, если UI процесс упал
659
+ ui_process = start_simple_ui()
660
 
661
  time.sleep(1)
662
  except KeyboardInterrupt:
663
  print("Принудительная остановка...")
664
  api_process.terminate()
665
+ if ui_process:
666
+ ui_process.terminate()
667
 
668
  if __name__ == "__main__":
669
  # Корректная обработка сигналов
simple_next_app.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // File: /tmp/ten_playground/app/page.js
2
+ // Simple Next.js app that redirects to API server
3
+ export default function Home() {
4
+ return (
5
+ <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
6
+ <h1>TEN Agent UI Loader</h1>
7
+ <p>API server is running at: <a href="http://localhost:8080">http://localhost:8080</a></p>
8
+
9
+ <div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0', borderRadius: '5px' }}>
10
+ <p>Используйте API напрямую:</p>
11
+ <ul>
12
+ <li><a href="http://localhost:8080/graphs">/graphs</a> - Список доступных графов</li>
13
+ <li><a href="http://localhost:8080/health">/health</a> - Статус API сервера</li>
14
+ </ul>
15
+ </div>
16
+
17
+ <div style={{ marginTop: '20px' }}>
18
+ <button
19
+ style={{
20
+ padding: '10px 15px',
21
+ backgroundColor: '#4CAF50',
22
+ color: 'white',
23
+ border: 'none',
24
+ borderRadius: '5px',
25
+ cursor: 'pointer'
26
+ }}
27
+ onClick={() => window.location.href = 'http://localhost:8080/graphs'}
28
+ >
29
+ Перейти к API
30
+ </button>
31
+ </div>
32
+ </div>
33
+ );
34
+ }
start.sh CHANGED
@@ -14,11 +14,17 @@ else
14
  PYTHON_CMD="python3"
15
  fi
16
 
17
- # Создаем необходимые директории
18
  echo "Creating temporary directories in /tmp..."
19
  mkdir -p /tmp/ten_user/agents
20
  mkdir -p /tmp/ten_user/logs
21
- chmod -R 777 /tmp/ten_user
 
 
 
 
 
 
22
 
23
  # Проверяем наличие файлов
24
  echo "Checking necessary files..."
 
14
  PYTHON_CMD="python3"
15
  fi
16
 
17
+ # Создаем необходимые директории без изменения прав
18
  echo "Creating temporary directories in /tmp..."
19
  mkdir -p /tmp/ten_user/agents
20
  mkdir -p /tmp/ten_user/logs
21
+ mkdir -p /tmp/ten_playground
22
+
23
+ # Проверяем, что директории созданы
24
+ echo "Checking directories..."
25
+ if [ -d "/tmp/ten_user" ]; then
26
+ echo "✅ /tmp/ten_user exists"
27
+ fi
28
 
29
  # Проверяем наличие файлов
30
  echo "Checking necessary files..."