דלגו לתוכן

תנו לסוכן מחשב לבדיקות

תחנה היא מכונה חד־פעמית שסוכן פותח כשהוא צריך פשוט לנסות משהו: לטעון דף, ללחוץ דרך אפליקציית שולחן עבודה, להקיש על טלפון. אתם יכולים לצפות בה עובדת בזמן אמת, ולהשתלט על הפקדים בכל רגע שתרצו.

תחנות של מסוף מבודד, דפדפן ושולחן עבודה הן מכונות וירטואליות עם דיסקים חד־פעמיים ורשתות מבוקרות בשער. Android ו-iOS הן אמולטורים מנוהלים במארח: מצב ההתקן שלהן חד־פעמי, אבל הרשת שלהן הולכת אחרי מארח השרת.

תחנות רצות במכונה שמארחת את cc_server — אותה מכונה, לא זו שאתם מביטים בה. יש חמישה משטחים גלויים למשתמש; משטחי שולחן העבודה והמובייל דורשים הגדרה במארח:

משטח רץ על הגדרה
טרמינל (VM) microVM שמאתחל תמונת Ubuntu מוצמדת אין — התמונה נמשכת אוטומטית בשימוש הראשון
דפדפן (VM) microVM שמאתחל תמונת Debian מוצמדת, עם Chromium, Firefox או WebKit שמותקנים בשימוש הראשון אין — אותו דבר
מחשב (VM) QEMU עם תמונת שולחן עבודה של Linux שכוללת Chromium, Firefox, WebKit וקו בסיס של CLI בסגנון ראנר מתארח ב-GitHub התקינו QEMU (brew install qemu), ואז ייבאו תמונת שולחן עבודה תחת הגדרות → שרת → תחנות
Android האמולטור של Google ל-Android התקינו את Android SDK, צרו AVD והפעילו אותו (להלן)
סימולטור iOS CoreSimulator במארח ה-Mac של השרת התקינו Xcode ואת סביבת ההרצה המבוקשת של iOS, ואז התקינו את גשר האוטומציה תחת הגדרות → שרת → תחנות

מכונות המסוף והדפדפן מאתחלות מתמונות מוצמדות לפי digest: הבתים המדויקים נקבעים מראש, נמשכים פעם אחת באתחול הראשון, ונשמרים במטמון — אחרי האתחול הראשון של כל סוג, מכונות מאוחרות יותר מדלגות לגמרי על ההורדה ועולות בתוך שניות. הגדרות → שרת → תחנות מציג מה השרת שלכם יכול לאתחל וכל מכונה שרצה כעת.

תמונת שולחן עבודה מותקנת נשארת ניתנת לניהול מאותו עמוד: ייבא מחליף אותה ב-qcow2 חדש יותר, ומחיקה מסירה אותה כדי שתוכלו להתקין בנייה חדשה.

התקני מובייל מוגדרים אחרת

קטע בשם “התקני מובייל מוגדרים אחרת”

אין תמונה משלנו ל-Android או ל-iOS, וזו אינה השמטה. תמונות המערכת של Android מגיעות מ-Android SDK של Google; סביבות ההרצה של iOS מגיעות מ-Xcode. עבור Android, התקינו את Android Studio (או את כלי ה-SDK של שורת הפקודה), הוסיפו התקן וירטואלי במנהל ההתקנים, והפעילו אותו. עמוד התחנות אז מדווח ש-Android זמין.

הוא גם אומר לכם בדיוק איזה שלב חסר — אין SDK, אין אמולטור, אין התקן וירטואלי, או התקן שפשוט אינו רץ — כי לארבעה מאלה יש ארבעה תיקונים שונים, ורק אחד מהם הוא הורדה. אם אתם מפעילים את cc_server ממעטפת שאינה רואה את ה-SDK, ייצאו אותו:

חלון מסוף
export ANDROID_HOME="$HOME/Library/Android/sdk"

עבור iOS, השרת עצמו חייב לרוץ על macOS עם Xcode וסביבת הרצה של סימולטור iOS מותקנת. פתחו את הגדרות → שרת → תחנות ולחצו על התקנת גשר האוטומציה של iOS. השרת מושך את ארטיפקט ה-WebDriverAgent המוצמד ב-checksum, מאמת אותו, ומתקין אותו במחסן האוטומציה שבבעלות השרת. אף סימולטור לא עולה במהלך ההגדרה.

מכונות חיות כלשוניות, לצד הצ’אט וה־diff — במרחב או בעמוד בקשת משיכה, באותו תפריט +, תחת מכונות:

  • מחשב — שולחן עבודה של Linux עם Chromium, Firefox ו-WebKit
  • Chromium, Firefox או WebKit — דפדפן
  • Android — אמולטור ה-Android שרץ
  • סימולטור iOS — סימולטור חד־פעמי שנוצר עבור השיחה הזו

בחרו אחת ולחצו על הפעל את המכונה. הלשונית אומרת באיזה שלב האתחול נמצא (“Starting the microVM”, “Waiting for the guest to come up”) במקום פשוט להסתובב.

הלשונית מוגבלת לשיחה, וכך גם כלי הסוכן. לשונית Chromium, Firefox או WebKit ו-browser_use,‏ Android ו-mobile_use, או סימולטור iOS ו-ios_use פונים לאותה מכונת ברירת מחדל. משבצות ממשק נוספות הן מכונות השוואה נפרדות; כלי הסוכן תמיד משתמשים בברירת המחדל.

כדי לבנות, להתקין ולהפעיל אפליקציית Flutter ב-Android:

חלון מסוף
fvm flutter build apk --debug

ואז קראו ל-mobile_use עם install_apk ועם build/app/outputs/flutter-apk/app-debug.apk, ואחריו start_app ושם החבילה של Android. ui_dump, צילומי מסך וקלט מתפעלים את אותו התקן שמוצג בלשונית האנושית. stop_app, clear_app_data, uninstall_app, open_url, וקריאות shell בצורת argv מכסים את שאר מחזור הפיתוח בלי להפעיל מעטפת במארח השרת.

כדי לבנות, להתקין ולהפעיל אפליקציית Flutter בסימולטור iOS:

חלון מסוף
fvm flutter build ios --simulator --debug
/usr/bin/plutil -extract CFBundleIdentifier raw -o - \
build/ios/iphonesimulator/Runner.app/Info.plist

ואז קראו ל-ios_use עם install_app ועם ספריית ה-.app, ואחריו start_app ומזהה החבילה שמודפס על ידי plutil. כלי ה-iOS חושף גם stop_app, uninstall_app, open_url, ופעולת spawn בצורת argv לאבחון מקומי בסימולטור.

תצוגות שולחן העבודה והדפדפן משנות גודל לפי הפאנל. iOS שומרת על גודל הנקודות הקבוע של הסימולטור ועוטפת אותו במסגרת. בכל משטח יש עדיין שני ערוצים בלתי תלויים: הסוכן מקבל צילומי מסך קטנים וזולים, ואתם מקבלים את התצוגה החיה. מעבר בין לשוניות עוצר את הערוץ האנושי, כך שמכונה ברקע לא עולה רוחב פס של תצוגה.

לשונית תחנה אינה מפעילה מכונה מעצמה, גם לא במהלך שחזור פריסה. הכלל הזה מונע מפתיחת האפליקציה לאתחל מכונות וירטואליות או ליצור התקני סימולטור בלי כוונה.

הקנבס פתוח להקלדה ולחיצות בכל עת שאף אחד אחר אינו מחזיק בשליטה בלעדית — או כשאתם מחזיקים בה. כל עוד אתם מחזיקים בנעילה, תג הסטטוס מציג השליטה בידיך, הסוכן עדיין יכול לצלם מסכים, והשרת מסרב לפעולות המשנות שלו בנקודה היחידה שכל פעולה עוברת דרכה. כל מה שאתם עושים נרשם על שמכם.

לחצו על עצור את המכונה כשסיימתם.

השליטה בלעדית במכוון: שני גורמים שמקלידים באותה מכונה מייצרים קלט שאף אחד מהם לא התכוון אליו.

העברת דברים פנימה והחוצה

קטע בשם “העברת דברים פנימה והחוצה”

העתקה והדבקה עובדות בשני הכיוונים על פני קנבס התחנה, וכך גם גרירת קבצים.

העתקה והדבקה. לחצו על צירוף ההעתקה מעל הקנבס, וכל מה שהמכונה העתיקה — טקסט, תמונה, רשימת קבצים — נוחת בלוח שלכם. הדבקה שולחת את שלכם לכיוון השני ולוחצת הדבקה בתוך המכונה. ב-macOS הצירוף הוא ⌘C/⌘V; ⌃C ממשיך להגיע למעטפת של האורח כאות ההפסקה. ב-Windows וב-Linux השתמשו ב-⌃C/⌃V — האורח מקבל את אותה הקלדה שהיה מקבל ממילא, כך ש-⌃C במסוף של האורח עדיין מפסיק, והלוח שלכם לא נגע.

שום דבר לא מסונכרן ברקע. סיסמה שאתם מעתיקים במחשב שלכם לא מגיעה לתוך מכונה וירטואלית מפני שטיימר הופעל; היא חוצה כשאתם מדביקים.

הדבקה לתוך תחנה מותרת כברירת מחדל. העתקה מתוך תחנה מבקשת אישור תחילה — אישור לעשר דקות, או לאפשר תמיד תחת הגדרות → שרת → תחנות → גישה ללוח. שני הכיוונים בלתי תלויים, והם הולכים אחריכם, לא אחרי סביבת העבודה.

גרירת קבצים פנימה. השליכו אותם בכל מקום על הקנבס.

  • בדפדפן (VM), הדף מקבל השלכה אמיתית בנקודה שבה שחררתם, כך שאזור העלאה מתנהג בדיוק כפי שהיה מתנהג עם קובץ מקומי.
  • במחשב (VM), הקבצים נוחתים ב-~/Drops והנתיבים שלהם נכנסים ללוח של המכונה, כך ש-⌃V מדביק אותם במנהל קבצים או בשדה העלאה. מארח אינו יכול לסנתז גרירה אל אפליקציית Linux כלשהי בלי להריץ דמון בעל הרשאות בתוך המכונה הווירטואלית, וזה בדיוק מה שלתחנות אין במכוון — ולכן הודעת הטוסט אומרת לכם לאן הקבצים הגיעו במקום להעמיד פנים שהתרחשה השלכה.
  • במסוף, הקובץ מועתק פנימה והנתיב שלו מוקלד בשורת הפקודה, מוכן להעברה לפקודה. הדבקת תמונה במסוף עושה את אותו הדבר: היא נשמרת במכונה ואתם מקבלים את הנתיב.

גרירת קבצים החוצה עובדת מהמחשב (VM). התחילו לגרור קובץ בתוך המכונה, הוציאו את הסמן מחוץ לקנבס, והוא הופך לגרירה אמיתית בשולחן העבודה שלכם — השליכו אותו ב-Finder או ב-Explorer. העתקת קבצים במנהל הקבצים של המכונה והדבקה בצד שלכם עובדת גם היא. הדפדפן (VM) אינו מציע זאת: לדפדפן ללא ממשק גרפי אין מטען גרירה לקרוא, ולנחש לפי בחירת הטקסט של הדף היה לחטוף לחיצות רגילות.

המסופים במרחב או בעמוד בקשת משיכה יכולים לרוץ בתוך תחנה במקום במכונה של השרת עצמו. כשהם רצים כך, הפאנל מציג תג VM במתחם מבודד.

מה משתנה:

  • שום דבר שאתם מריצים לא מגיע למארח. rm -rf שם עולה לכם את התחנה.
  • הרשת דוחה כברירת מחדל. רק מארחים ברשימה המותרת של התחנה נגישים, והכול עובר דרך שער שאוכף אותה.
  • git push עדיין עובד במסוף שאתם פותחים בעצמכם. לתחנה אין אישור שמור; היא מבקשת מהשרת אישור קצר-מועד לכל פעולה, מוגבל לגישה שלכם עצמכם למארח הקוד, והאסימון הזה מבוטל כשהתחנה נסגרת. תחנה שסוכן פותח מקבלת אישור לקריאה בלבד, ולכן היא יכולה למשוך אבל לא לדחוף.

מה שכדאי לדעת:

  • מסוף מבודד משוחזר ממתין לכם. כמו לשונית תחנה, מסוף microvm שחוזר עם הפריסה שלכם מציג כפתור פתח את ה־shell במקום לאתחל את המכונה הווירטואלית שלו עם העלייה. מסוף שרץ במעטפת של המארח עולה תהליך אחד; מסוף מבודד עולה מכונה שלמה.
  • קבצים שמושלכים על המסוף מוקלדים, לא נפתחים. השליכו CSV על מסוף מבודד והוא מועתק לתוך המכונה ונתיב האורח שלו מופיע בשורת הפקודה — נתיב המארח היה פקודה שנכשלת שם.
  • העותק בתחנה הוא עותק. עץ העבודה שלכם במארח נשאר הקובע. קומיטים שאתם עושים בתוך תחנה נאספים אל refs/rigs/<id>/* כשאתם מבקשים — הם לעולם לא מזיזים את הענף שלכם ולא נוגעים בעץ העבודה שלכם.
  • עבודה שמכוונת למארח עדיין רצה במארח. אורח Linux אינו יכול לבנות עבור macOS או להריץ xcodebuild. בחרו לשם כך במפורש במנוע המקורי; האפליקציה תבקש מכם לאשר במקום לעבור בשקט.

הריצו שרת פיתוח בטרמינל (VM) — pnpm dev על פורט 3000, נניח — ותוך שניות ספורות סמל התקע בפינת המסוף נדלק: הפורט מועבר אל localhost:3000 במכונה שלכם, נגיש ב-localhost:3000 בתוך הדפדפן (VM), ניתן לשיתוף ברשת שלכם, ואפשר לתת לו שם כמו https://myapp.test. כל הזרימה — העברה אוטומטית, שיתוף ברשת המקומית, דומייני פיתוח עם HTTPS — זוכה למדריך משלה: העברת פורטים ממסוף.

סביבת עבודה יכולה לכוון את הטרמינל (VM) ואת הדפדפן (VM) לתמונות משלה — תחת הגדרות → שרת → תחנות → תמונות מותאמות אישית (סביבת עבודה זו). שימוש אופייני: להרחיב את תמונת ברירת המחדל עם שרשרת הכלים שהפרויקט שלכם צריך, כך שכל מסוף עולה מוכן, או לעשות שימוש חוזר בתמונה שהצוות שלכם כבר מפרסם.

המכונה עדיין מאתחלת בתוך אותו מתחם מבודד: אותו שער תעבורה יוצאת, אותו מתווך אישורי גישה, אותו מחזור חיים. רק מנהלי סביבת העבודה יכולים לשנות את ההגדרה, מכונות חדשות מאמצות אותה (רצות שומרות על שלהן), וההפניה חייבת להיות תמונת registry — נתיבים מקומיים נדחים.

מה תמונה חייבת לספק:

  • טרמינל (VM) — תמונת Linux עם bash. היא זקוקה גם ל-git, ל-curl ול-socat — מותקנים מראש, או ניתנים להתקנה בהתנעה הראשונה (כל תמונה מבוססת Debian/Ubuntu מתאימה: הם מותקנים אוטומטית, יחד עם ערכת כלי CLI בסגנון ראנר מתארח ב-GitHub: מהדרים, Python 3, jq, כלי ארכיון, rsync. סביבות הרצה של שפות כמו Node/Go/Java ו-Docker אינן מותקנות מראש). פורט 7911 של האורח שמור למעביר הפורטים.
  • דפדפן (VM) — תמונת Debian/Ubuntu עם bash ו-socat, ובנוסף קובץ הרצה chromium. השרת מפעיל את Chromium בעצמו כ-chromium --headless=new ומעביר DevTools מפורט 9223 של האורח אל 9222. PulseAudio ו-ffmpeg מותקנים בהתנעה הראשונה. אל תבנו FROM chromedp/headless-shell — לקובץ ההרצה הזה אין אודיו. העקיפה חלה על תחנות Chromium בלבד; Firefox ו-WebKit מתעלמים ממנה ומאתחלים את תמונת ה-Debian המוצמדת. פורטים 9222/9223 של האורח שמורים.

שתי המלצות: להצמיד את ההפניה לפי digest (@sha256:…) כדי שהבתים לא ישתנו מתחת לרגליכם, ולאחסן אותה ב-Docker Hub או ב-GitHub Container Registry — מארחי ההורדה של ה-registries האלה מתקבלים דרך שער התעבורה היוצאת אוטומטית; אחרים עשויים להזדקק לכך שמארחי אספקת התוכן שלהם יהיו נגישים כדי למשוך.

תחנות נועדו להיות זמניות:

  • חוסר פעילות — אחרי כ־15 דקות בלי שום פעילות, תחנת דפדפן, שולחן עבודה או מובייל נחנית (קפואה, מתעוררת מיד בפעולה הבאה). מסוף מבודד ממתין 45 דקות. אחרי כפול מחלון חוסר הפעילות, הוא נסגר.
  • מגבלת זמן — לכל תחנה יש אורך חיים קשיח שאינה יכולה להאריך, ולא משנה מה היא עושה.
  • זיכרון — אם השרת זקוק למקום לתחנה חדשה, הוא סוגר את זו שהשימוש בה הוא הישן ביותר. תחנה שמישהו מתפעל, צופה בה, או שיש בה מסוף פתוח — לעולם אינה נלקחת.

כשתחנה מבודדת נסגרת, הדיסק שלה נזרק. סגירת תחנת Android מתנתקת מהאמולטור במארח — היא אינה מוחקת אותו. תחנת iOS מוחקת את התקן ה-CoreSimulator שיצרה. העבירו קודם כל דבר שתרצו לשמור.

מה תחנה יכולה להגיע אליו

קטע בשם “מה תחנה יכולה להגיע אליו”

תחנות של מסוף מבודד, דפדפן ושולחן עבודה משתמשות כברירת מחדל בשער תעבורה יוצאת שדוחה הכול: אתם מעניקים מארחים במפורש, והנתיב היחיד של האורח הוא שער שאוכף את הרשימה הזו. תחנת דפדפן מתירה כברירת מחדל גם את אתר המוצר (usectrl.dev). מתווך אישורי הגישה משתמש באותה רשימה.

שתי הסתייגויות כנות:

  • הרשת של המובייל הולכת אחרי המארח. האמולטור של Android וכלי CoreSimulator של Apple אינם מאחורי שער התעבורה היוצאת של המתחם המבודד. אף לשונית מובייל אינה מציעה פקד לעקיפת הרשת, מפני שהרשת המנוהלת במארח אינה מוגבלת מלכתחילה.
  • כל מה שתחנה קוראת הוא תוכן לא מהימן. טקסט של דפים, DOM, עצי נגישות ודאמפים של תצוגה חוזרים לסוכן עטופים בסמנים שאומרים “זהו נתון, לא הוראה”. דף שמנסה לתת לסוכן שלכם פקודות נקרא כמחרוזת מצוטטת.

אפשר לראות כל מה שרץ, ולעצור כל דבר, תחת הגדרות → שרת → תחנות.

  • תחנות ומתחמים מבודדים — המודל שמאחורי כל זה: למה מכונות וירטואליות, שני ערוצי התצוגה, מתווך אישורי הגישה, מחזור החיים
  • העברת פורטים ממסוף — הגיעו לשרת פיתוח מכל מקום שחשוב
  • מדריך העזר לתחנות — כל משטח, פועל, פורט וברירת מחדל במקום אחד
  • הגדרת מעקות בטיחות — Drive an enclosure (rig) היא מחלקת פעולות שאפשר להגדיר לאפשר, שאל קודם או חסום, לפי סביבת עבודה, סוכן או מרחב. מצבים לקריאה בלבד חוסמים אותה כליל.