clawbench-cli 0.1.2__py3-none-any.whl
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.
- clawbench/__init__.py +35 -0
- clawbench/__main__.py +8 -0
- clawbench/batch.py +619 -0
- clawbench/cli.py +397 -0
- clawbench/data/chrome-extension/README.md +127 -0
- clawbench/data/chrome-extension/background.js +50 -0
- clawbench/data/chrome-extension/content.js +70 -0
- clawbench/data/chrome-extension/manifest.json +25 -0
- clawbench/data/chrome-extension/setup.sh +27 -0
- clawbench/data/chrome-extension/stealth.js +200 -0
- clawbench/data/docker/Dockerfile +51 -0
- clawbench/data/docker/entrypoint.sh +394 -0
- clawbench/data/docker/setup-openclaw.sh +112 -0
- clawbench/data/eval/README.md +95 -0
- clawbench/data/eval/agentic_eval.md +53 -0
- clawbench/data/extension-server/.python-version +1 -0
- clawbench/data/extension-server/README.md +54 -0
- clawbench/data/extension-server/pyproject.toml +7 -0
- clawbench/data/extension-server/server.py +360 -0
- clawbench/data/extension-server/uv.lock +644 -0
- clawbench/data/models/model.schema.json +44 -0
- clawbench/data/models/models.example.yaml +16 -0
- clawbench/data/shared/alex_green_personal_info.json +451 -0
- clawbench/data/test-cases/001-daily-life-food-uber-eats/task.json +25 -0
- clawbench/data/test-cases/002-daily-life-food-doordash/task.json +25 -0
- clawbench/data/test-cases/004-daily-life-food-instacart/extra_info/grocery_list.json +36 -0
- clawbench/data/test-cases/004-daily-life-food-instacart/task.json +30 -0
- clawbench/data/test-cases/006-daily-life-food-uber-eats/task.json +24 -0
- clawbench/data/test-cases/007-daily-life-food-instacart/extra_info/meal_plan.json +21 -0
- clawbench/data/test-cases/007-daily-life-food-instacart/task.json +30 -0
- clawbench/data/test-cases/011-daily-life-housing-zillow/task.json +25 -0
- clawbench/data/test-cases/015-daily-life-housing-craigslist/extra_info/listing_details.json +26 -0
- clawbench/data/test-cases/015-daily-life-housing-craigslist/task.json +30 -0
- clawbench/data/test-cases/035-daily-life-health-medical-betterhelp/task.json +25 -0
- clawbench/data/test-cases/041-daily-life-pets-rover/task.json +25 -0
- clawbench/data/test-cases/043-daily-life-pets-rover/extra_info/pet_info.json +12 -0
- clawbench/data/test-cases/043-daily-life-pets-rover/task.json +30 -0
- clawbench/data/test-cases/045-daily-life-personal-care-booksy/task.json +25 -0
- clawbench/data/test-cases/047-daily-life-personal-care-taskrabbit/extra_info/address_info.json +7 -0
- clawbench/data/test-cases/047-daily-life-personal-care-taskrabbit/task.json +30 -0
- clawbench/data/test-cases/086-job-search-hr-cv-autofill-greenhouse-meta/extra_info/job_links.json +5 -0
- clawbench/data/test-cases/086-job-search-hr-cv-autofill-greenhouse-meta/task.json +30 -0
- clawbench/data/test-cases/089-job-search-hr-cv-autofill-simplify-jobs/extra_info/job_links.json +5 -0
- clawbench/data/test-cases/089-job-search-hr-cv-autofill-simplify-jobs/task.json +30 -0
- clawbench/data/test-cases/091-job-search-hr-job-apply-indeed/task.json +25 -0
- clawbench/data/test-cases/120-office-secretary-tasks-email-mgmt-purelymail/task.json +28 -0
- clawbench/data/test-cases/121-office-secretary-tasks-email-mgmt-purelymail/task.json +28 -0
- clawbench/data/test-cases/128-office-secretary-tasks-email-mgmt-purelymail/task.json +28 -0
- clawbench/data/test-cases/134-office-secretary-tasks-calendar-calendly/task.json +25 -0
- clawbench/data/test-cases/137-office-secretary-tasks-calendar-doodle/extra_info/meeting_details.json +30 -0
- clawbench/data/test-cases/137-office-secretary-tasks-calendar-doodle/task.json +30 -0
- clawbench/data/test-cases/139-office-secretary-tasks-calendar-calendly/task.json +25 -0
- clawbench/data/test-cases/142-office-secretary-tasks-collab-trello/extra_info/task_list.json +29 -0
- clawbench/data/test-cases/142-office-secretary-tasks-collab-trello/task.json +30 -0
- clawbench/data/test-cases/179-dev-tech-github-ops-github/extra_info/config.json +13 -0
- clawbench/data/test-cases/179-dev-tech-github-ops-github/task.json +30 -0
- clawbench/data/test-cases/180-dev-tech-github-ops-github/task.json +25 -0
- clawbench/data/test-cases/215-academia-research-paper-tables-overleaf/extra_info/raw_results.json +47 -0
- clawbench/data/test-cases/215-academia-research-paper-tables-overleaf/task.json +30 -0
- clawbench/data/test-cases/242-academia-research-research-tools-overleaf/task.json +25 -0
- clawbench/data/test-cases/246-academia-research-research-tools-zotero/task.json +25 -0
- clawbench/data/test-cases/247-academia-research-research-tools-semantic-scholar/task.json +25 -0
- clawbench/data/test-cases/265-education-learning-general-coursera/task.json +25 -0
- clawbench/data/test-cases/266-education-learning-general-leetcode/extra_info/solution_code.py +9 -0
- clawbench/data/test-cases/266-education-learning-general-leetcode/task.json +30 -0
- clawbench/data/test-cases/273-education-learning-general-edx/task.json +25 -0
- clawbench/data/test-cases/274-education-learning-general-udemy/task.json +25 -0
- clawbench/data/test-cases/279-travel-general-airbnb/task.json +25 -0
- clawbench/data/test-cases/280-travel-general-booking-com/task.json +25 -0
- clawbench/data/test-cases/363-entertainment-hobbies-general-ticketmaster/task.json +25 -0
- clawbench/data/test-cases/369-entertainment-hobbies-general-goodreads/extra_info/book_list.json +14 -0
- clawbench/data/test-cases/369-entertainment-hobbies-general-goodreads/task.json +30 -0
- clawbench/data/test-cases/372-entertainment-hobbies-general-eventbrite/extra_info/event_details.json +10 -0
- clawbench/data/test-cases/372-entertainment-hobbies-general-eventbrite/task.json +30 -0
- clawbench/data/test-cases/403-personal-management-account-security-1password-web/extra_info/credentials.json +34 -0
- clawbench/data/test-cases/403-personal-management-account-security-1password-web/task.json +30 -0
- clawbench/data/test-cases/413-personal-management-personal-tools-todoist/extra_info/task_list.json +52 -0
- clawbench/data/test-cases/413-personal-management-personal-tools-todoist/task.json +30 -0
- clawbench/data/test-cases/468-rating-voting-general-glassdoor/extra_info/interview_experience.json +10 -0
- clawbench/data/test-cases/468-rating-voting-general-glassdoor/task.json +30 -0
- clawbench/data/test-cases/469-rating-voting-general-tripadvisor/extra_info/review_content.json +6 -0
- clawbench/data/test-cases/469-rating-voting-general-tripadvisor/task.json +30 -0
- clawbench/data/test-cases/470-rating-voting-general-trustpilot/extra_info/review_content.json +6 -0
- clawbench/data/test-cases/470-rating-voting-general-trustpilot/task.json +30 -0
- clawbench/data/test-cases/474-rating-voting-general-capterra/task.json +25 -0
- clawbench/data/test-cases/475-rating-voting-general-g2/task.json +25 -0
- clawbench/data/test-cases/482-creation-init-general-confluence/extra_info/content.json +3 -0
- clawbench/data/test-cases/482-creation-init-general-confluence/task.json +30 -0
- clawbench/data/test-cases/483-creation-init-general-airtable/task.json +25 -0
- clawbench/data/test-cases/484-creation-init-general-clickup/task.json +28 -0
- clawbench/data/test-cases/485-creation-init-general-webflow/task.json +25 -0
- clawbench/data/test-cases/486-creation-init-general-mailchimp/extra_info/content.json +3 -0
- clawbench/data/test-cases/486-creation-init-general-mailchimp/task.json +30 -0
- clawbench/data/test-cases/487-creation-init-general-typeform/extra_info/survey_questions.json +85 -0
- clawbench/data/test-cases/487-creation-init-general-typeform/task.json +30 -0
- clawbench/data/test-cases/488-creation-init-general-substack/extra_info/content.json +3 -0
- clawbench/data/test-cases/488-creation-init-general-substack/task.json +30 -0
- clawbench/data/test-cases/489-creation-init-general-ghost/extra_info/content.json +3 -0
- clawbench/data/test-cases/489-creation-init-general-ghost/task.json +30 -0
- clawbench/data/test-cases/501-creation-init-general-asana/extra_info/project_description.json +8 -0
- clawbench/data/test-cases/501-creation-init-general-asana/task.json +33 -0
- clawbench/data/test-cases/529-daily-life-shopping-delivery-king-arthur-baking/task.json +25 -0
- clawbench/data/test-cases/533-daily-life-utilities-inmyarea/task.json +25 -0
- clawbench/data/test-cases/535-daily-life-home-home-depot/task.json +25 -0
- clawbench/data/test-cases/537-daily-life-food-crumbl/task.json +25 -0
- clawbench/data/test-cases/539-daily-life-health-jefit/task.json +25 -0
- clawbench/data/test-cases/542-daily-life-pets-wag/task.json +25 -0
- clawbench/data/test-cases/551-finance-investment-crypto-wallet-trezor/task.json +25 -0
- clawbench/data/test-cases/552-finance-investment-business-payment-plooto/task.json +25 -0
- clawbench/data/test-cases/555-finance-investment-insurance-insureon/task.json +25 -0
- clawbench/data/test-cases/559-finance-investment-crowdfunding-frontfundr/task.json +25 -0
- clawbench/data/test-cases/564-daily-life-event-registration-race-roster/task.json +25 -0
- clawbench/data/test-cases/565-job-search-hr-job-search-jopwell/task.json +25 -0
- clawbench/data/test-cases/566-job-search-hr-job-search-ziprecruiter/extra_info/listing_details.json +26 -0
- clawbench/data/test-cases/566-job-search-hr-job-search-ziprecruiter/task.json +30 -0
- clawbench/data/test-cases/569-job-search-hr-job-search-careerbuilder/task.json +25 -0
- clawbench/data/test-cases/570-job-search-hr-job-search-hired/task.json +25 -0
- clawbench/data/test-cases/571-job-search-hr-recruitment-mgmt-workable/extra_info/listing_details.json +26 -0
- clawbench/data/test-cases/571-job-search-hr-recruitment-mgmt-workable/task.json +30 -0
- clawbench/data/test-cases/576-office-secretary-tasks-reports-ftc-reportfraud/task.json +25 -0
- clawbench/data/test-cases/583-office-secretary-tasks-support-tickets-freshdesk/task.json +25 -0
- clawbench/data/test-cases/598-academia-research-legal-docs-formswift/task.json +25 -0
- clawbench/data/test-cases/606-education-learning-kids-courses-outschool/task.json +25 -0
- clawbench/data/test-cases/607-education-learning-art-courses-creativebug/task.json +25 -0
- clawbench/data/test-cases/609-education-learning-meditation-spirit-rock-meditation-center/task.json +25 -0
- clawbench/data/test-cases/615-travel-flights-spirit-airlines/task.json +25 -0
- clawbench/data/test-cases/618-travel-train-bus-12go-asia/task.json +25 -0
- clawbench/data/test-cases/625-travel-camping-outdoor-parks-canada-reservations/task.json +25 -0
- clawbench/data/test-cases/626-travel-bus-flixbus/task.json +25 -0
- clawbench/data/test-cases/627-travel-flights-momondo/task.json +25 -0
- clawbench/data/test-cases/632-shopping-commerce-beauty-care-olaplex/task.json +25 -0
- clawbench/data/test-cases/634-shopping-commerce-apparel-dooney-bourke/task.json +25 -0
- clawbench/data/test-cases/635-shopping-commerce-gifts-uncommon-goods/task.json +25 -0
- clawbench/data/test-cases/636-shopping-commerce-auto-parts-rockauto/task.json +25 -0
- clawbench/data/test-cases/638-shopping-commerce-print-custom-vistaprint/task.json +25 -0
- clawbench/data/test-cases/639-shopping-commerce-luxury-mansur-gavriel/task.json +25 -0
- clawbench/data/test-cases/671-entertainment-gaming-humble-bundle/task.json +25 -0
- clawbench/data/test-cases/672-entertainment-hobbies-anime-streaming-crunchyroll/task.json +25 -0
- clawbench/data/test-cases/674-entertainment-hobbies-masterclass-masterclass/task.json +25 -0
- clawbench/data/test-cases/676-government-civic-legal-docs-legalnature/task.json +25 -0
- clawbench/data/test-cases/685-personal-management-budget-mgmt-everydollar/task.json +25 -0
- clawbench/data/test-cases/687-personal-management-vpn-subscription-ipvanish/task.json +25 -0
- clawbench/data/test-cases/688-personal-management-insurance-compare-insurify/task.json +25 -0
- clawbench/data/test-cases/695-automation-workflows-recurring-order-stumptown-coffee/task.json +25 -0
- clawbench/data/test-cases/697-automation-workflows-recurring-order-bean-box/task.json +25 -0
- clawbench/data/test-cases/699-automation-workflows-recurring-order-mistobox/task.json +25 -0
- clawbench/data/test-cases/700-deletion-revocation-data-deletion-deleteme/task.json +25 -0
- clawbench/data/test-cases/705-rating-voting-wine-review-vivino/task.json +25 -0
- clawbench/data/test-cases/706-rating-voting-beer-review-beeradvocate/task.json +25 -0
- clawbench/data/test-cases/707-rating-voting-social-wine-untappd/task.json +25 -0
- clawbench/data/test-cases/708-rating-voting-professor-review-ratemyprofessors/task.json +28 -0
- clawbench/data/test-cases/709-rating-voting-service-review-angi/task.json +25 -0
- clawbench/data/test-cases/710-creation-init-interior-design-roomsketcher/task.json +25 -0
- clawbench/data/test-cases/711-creation-init-color-design-coolors/task.json +25 -0
- clawbench/data/test-cases/712-creation-init-website-create-squarespace/task.json +25 -0
- clawbench/data/test-cases/713-creation-init-website-build-wix/task.json +25 -0
- clawbench/data/test-cases/735-home-services-maintenance-house-cleaning-bark/task.json +25 -0
- clawbench/data/test-cases/736-home-services-maintenance-plumbing-ace-hardware/task.json +25 -0
- clawbench/data/test-cases/737-home-services-maintenance-kitchen-remodel-lowes/task.json +25 -0
- clawbench/data/test-cases/738-home-services-maintenance-equipment-install-amazon-home-services/task.json +25 -0
- clawbench/data/test-cases/750-automotive-vehicle-services-car-insurance-compare-kanetix/task.json +25 -0
- clawbench/data/test-cases/751-automotive-vehicle-services-car-lease-sixt/task.json +25 -0
- clawbench/data/test-cases/754-automotive-vehicle-services-used-car-listing-autotrader/task.json +25 -0
- clawbench/data/test-cases/763-automotive-vehicle-services-car-lease-autoslash/task.json +25 -0
- clawbench/data/test-cases/766-nonprofit-charity-donation-doctors-without-borders-msf/task.json +25 -0
- clawbench/data/test-cases/768-nonprofit-charity-community-crowdfund-ioby/task.json +25 -0
- clawbench/data/test-cases/770-nonprofit-charity-volunteer-apply-on-make-a-wish-foundation-website-complete-and-submit-a-volunteer-application-form-selecting-the-wish-granter-role-and-entering-city-phoenix-az/task.json +25 -0
- clawbench/data/test-cases/774-nonprofit-charity-nonprofit-job-apply-charity-village/task.json +25 -0
- clawbench/data/test-cases/776-nonprofit-charity-volunteer-signup-idealist/task.json +25 -0
- clawbench/data/test-cases/778-nonprofit-charity-donation-globalgiving/extra_info/payment_info.json +3 -0
- clawbench/data/test-cases/778-nonprofit-charity-donation-globalgiving/task.json +30 -0
- clawbench/data/test-cases/780-beauty-personal-care-skincare-purchase-soko-glam/extra_info/address_info.json +4 -0
- clawbench/data/test-cases/780-beauty-personal-care-skincare-purchase-soko-glam/task.json +30 -0
- clawbench/data/test-cases/781-beauty-personal-care-beauty-booking-bluemercury/extra_info/email_info.json +3 -0
- clawbench/data/test-cases/781-beauty-personal-care-beauty-booking-bluemercury/task.json +30 -0
- clawbench/data/test-cases/782-beauty-personal-care-skincare-purchase-paulas-choice/task.json +24 -0
- clawbench/data/test-cases/783-beauty-personal-care-beauty-booking-ulta-beauty/task.json +24 -0
- clawbench/data/test-cases/785-beauty-personal-care-skincare-curology/task.json +25 -0
- clawbench/data/test-cases/788-beauty-personal-care-makeup-the-ordinary/task.json +25 -0
- clawbench/data/test-cases/789-beauty-personal-care-makeup-fenty-beauty/task.json +25 -0
- clawbench/data/test-cases/793-beauty-personal-care-beauty-retail-mac-cosmetics/task.json +25 -0
- clawbench/data/test-cases/794-beauty-personal-care-salon-booking-styleseat/task.json +25 -0
- clawbench/data/test-cases/795-pet-animal-care-pet-adoption-aspca/task.json +25 -0
- clawbench/data/test-cases/796-pet-animal-care-pet-supplies-grooming-petsmart/extra_info/pet_info.json +12 -0
- clawbench/data/test-cases/796-pet-animal-care-pet-supplies-grooming-petsmart/task.json +30 -0
- clawbench/data/test-cases/799-pet-animal-care-pet-insurance-aspca-pet-health-insurance/task.json +25 -0
- clawbench/data/test-cases/801-pet-animal-care-pet-friendly-travel-bringfido/task.json +25 -0
- clawbench/data/test-cases/803-pet-animal-care-pet-medical-pawp/extra_info/pet_info.json +12 -0
- clawbench/data/test-cases/803-pet-animal-care-pet-medical-pawp/task.json +30 -0
- clawbench/data/test-cases/807-pet-animal-care-pet-dna-embark/task.json +25 -0
- clawbench/data/test-cases/809-pet-animal-care-pet-adopt-petfinder/task.json +28 -0
- clawbench/data/test-cases/812-pet-animal-care-pet-subscription-ollie/task.json +25 -0
- clawbench/data/test-cases/815-personal-management-records-mgmt-myheritage/task.json +25 -0
- clawbench/data/test-cases/821-education-learning-reading-self-study-blinkist/task.json +25 -0
- clawbench/data/test-cases/861-entertainment-hobbies-movies-cineplex/task.json +25 -0
- clawbench/data/test-cases/862-entertainment-hobbies-movies-amc-theatres/task.json +25 -0
- clawbench/data/test-cases/864-entertainment-hobbies-show-tickets-ticketmaster/task.json +25 -0
- clawbench/data/test-cases/865-travel-outdoor-hipcamp/task.json +25 -0
- clawbench/data/test-cases/867-entertainment-hobbies-movies-fandango/task.json +25 -0
- clawbench/data/test-cases/872-daily-life-food-opentable/task.json +25 -0
- clawbench/data/test-cases/873-daily-life-food-resy/task.json +28 -0
- clawbench/data/test-cases/876-entertainment-hobbies-show-tickets-vivid-seats/task.json +25 -0
- clawbench/data/test-cases/877-entertainment-hobbies-show-tickets-stubhub/task.json +25 -0
- clawbench/data/test-cases/878-travel-outdoor-ontario-parks/task.json +25 -0
- clawbench/data/test-cases/883-education-learning-hobby-class-sur-la-table/task.json +25 -0
- clawbench/data/test-cases/884-entertainment-hobbies-experience-breakout-games/task.json +25 -0
- clawbench/data/test-cases/885-entertainment-hobbies-experience-bowlero/task.json +25 -0
- clawbench/data/test-cases/886-entertainment-hobbies-experience-topgolf/task.json +25 -0
- clawbench/data/test-cases/lite.json +226 -0
- clawbench/data/test-cases/lite.schema.json +105 -0
- clawbench/data/test-cases/task.schema.json +132 -0
- clawbench/data/tools/build_clawbench_lite_enc.py +161 -0
- clawbench/doctor.py +171 -0
- clawbench/engine.py +180 -0
- clawbench/generate_resume_pdf.py +140 -0
- clawbench/hf_upload.py +78 -0
- clawbench/image.py +127 -0
- clawbench/paths.py +150 -0
- clawbench/resume_template.json +104 -0
- clawbench/run.py +942 -0
- clawbench/tui.py +1401 -0
- clawbench_cli-0.1.2.dist-info/METADATA +770 -0
- clawbench_cli-0.1.2.dist-info/RECORD +226 -0
- clawbench_cli-0.1.2.dist-info/WHEEL +4 -0
- clawbench_cli-0.1.2.dist-info/entry_points.txt +4 -0
- clawbench_cli-0.1.2.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
EXT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
|
|
6
|
+
# Detect Chrome binary
|
|
7
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
8
|
+
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
|
9
|
+
elif command -v google-chrome-stable &>/dev/null; then
|
|
10
|
+
CHROME="google-chrome-stable"
|
|
11
|
+
elif command -v google-chrome &>/dev/null; then
|
|
12
|
+
CHROME="google-chrome"
|
|
13
|
+
elif command -v chromium-browser &>/dev/null; then
|
|
14
|
+
CHROME="chromium-browser"
|
|
15
|
+
elif command -v chromium &>/dev/null; then
|
|
16
|
+
CHROME="chromium"
|
|
17
|
+
else
|
|
18
|
+
echo "Chrome not found" && exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
"$CHROME" \
|
|
22
|
+
--no-first-run \
|
|
23
|
+
--disable-default-apps \
|
|
24
|
+
--remote-debugging-port=9222 \
|
|
25
|
+
--load-extension="$EXT_DIR" \
|
|
26
|
+
--disable-extensions-except="$EXT_DIR" \
|
|
27
|
+
"$@"
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stealth.js — Anti-bot-detection patches.
|
|
3
|
+
*
|
|
4
|
+
* Injected at document_start in the MAIN world (see manifest.json).
|
|
5
|
+
* Must run before any page script to reliably override browser fingerprints.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
(function () {
|
|
9
|
+
"use strict";
|
|
10
|
+
|
|
11
|
+
// 1. navigator.webdriver → false (real Chrome returns false, not undefined)
|
|
12
|
+
try {
|
|
13
|
+
Object.defineProperty(navigator, "webdriver", {
|
|
14
|
+
get: () => false,
|
|
15
|
+
configurable: true,
|
|
16
|
+
});
|
|
17
|
+
} catch (_) {}
|
|
18
|
+
|
|
19
|
+
// 2. navigator.languages — ensure realistic value
|
|
20
|
+
try {
|
|
21
|
+
Object.defineProperty(navigator, "languages", {
|
|
22
|
+
get: () => ["en-US", "en"],
|
|
23
|
+
configurable: true,
|
|
24
|
+
});
|
|
25
|
+
} catch (_) {}
|
|
26
|
+
|
|
27
|
+
// 3. navigator.plugins — fake standard Chrome plugins
|
|
28
|
+
try {
|
|
29
|
+
const fakePlugins = [
|
|
30
|
+
{ name: "Chrome PDF Plugin", filename: "internal-pdf-viewer", description: "Portable Document Format" },
|
|
31
|
+
{ name: "Chrome PDF Viewer", filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", description: "" },
|
|
32
|
+
{ name: "Native Client", filename: "internal-nacl-plugin", description: "" },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const pluginArray = Object.create(PluginArray.prototype);
|
|
36
|
+
fakePlugins.forEach((p, i) => {
|
|
37
|
+
const plugin = Object.create(Plugin.prototype);
|
|
38
|
+
Object.defineProperties(plugin, {
|
|
39
|
+
name: { get: () => p.name },
|
|
40
|
+
filename: { get: () => p.filename },
|
|
41
|
+
description: { get: () => p.description },
|
|
42
|
+
length: { get: () => 0 },
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(pluginArray, i, { get: () => plugin, enumerable: true });
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(pluginArray, "length", { get: () => fakePlugins.length });
|
|
47
|
+
pluginArray.item = (i) => pluginArray[i] || null;
|
|
48
|
+
pluginArray.namedItem = (name) => {
|
|
49
|
+
const idx = fakePlugins.findIndex((p) => p.name === name);
|
|
50
|
+
return idx >= 0 ? pluginArray[idx] : null;
|
|
51
|
+
};
|
|
52
|
+
pluginArray.refresh = () => {};
|
|
53
|
+
pluginArray[Symbol.iterator] = function* () {
|
|
54
|
+
for (let i = 0; i < fakePlugins.length; i++) yield pluginArray[i];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
Object.defineProperty(navigator, "plugins", {
|
|
58
|
+
get: () => pluginArray,
|
|
59
|
+
configurable: true,
|
|
60
|
+
});
|
|
61
|
+
} catch (_) {}
|
|
62
|
+
|
|
63
|
+
// 4. navigator.mimeTypes — match the fake plugins
|
|
64
|
+
try {
|
|
65
|
+
const fakeMimeTypes = [
|
|
66
|
+
{ type: "application/pdf", suffixes: "pdf", description: "Portable Document Format" },
|
|
67
|
+
{ type: "application/x-google-chrome-pdf", suffixes: "pdf", description: "Portable Document Format" },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const mimeTypeArray = Object.create(MimeTypeArray.prototype);
|
|
71
|
+
fakeMimeTypes.forEach((m, i) => {
|
|
72
|
+
const mimeType = Object.create(MimeType.prototype);
|
|
73
|
+
Object.defineProperties(mimeType, {
|
|
74
|
+
type: { get: () => m.type },
|
|
75
|
+
suffixes: { get: () => m.suffixes },
|
|
76
|
+
description: { get: () => m.description },
|
|
77
|
+
enabledPlugin: { get: () => null },
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(mimeTypeArray, i, { get: () => mimeType, enumerable: true });
|
|
80
|
+
});
|
|
81
|
+
Object.defineProperty(mimeTypeArray, "length", { get: () => fakeMimeTypes.length });
|
|
82
|
+
mimeTypeArray.item = (i) => mimeTypeArray[i] || null;
|
|
83
|
+
mimeTypeArray.namedItem = (name) => {
|
|
84
|
+
const idx = fakeMimeTypes.findIndex((m) => m.type === name);
|
|
85
|
+
return idx >= 0 ? mimeTypeArray[idx] : null;
|
|
86
|
+
};
|
|
87
|
+
mimeTypeArray[Symbol.iterator] = function* () {
|
|
88
|
+
for (let i = 0; i < fakeMimeTypes.length; i++) yield mimeTypeArray[i];
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
Object.defineProperty(navigator, "mimeTypes", {
|
|
92
|
+
get: () => mimeTypeArray,
|
|
93
|
+
configurable: true,
|
|
94
|
+
});
|
|
95
|
+
} catch (_) {}
|
|
96
|
+
|
|
97
|
+
// 5. WebGL renderer/vendor spoofing
|
|
98
|
+
try {
|
|
99
|
+
const WEBGL_VENDOR = "Google Inc. (Google)";
|
|
100
|
+
const WEBGL_RENDERER =
|
|
101
|
+
"ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver)";
|
|
102
|
+
|
|
103
|
+
const originalGetParameter = WebGLRenderingContext.prototype.getParameter;
|
|
104
|
+
WebGLRenderingContext.prototype.getParameter = function (param) {
|
|
105
|
+
if (param === 0x9245) return WEBGL_VENDOR;
|
|
106
|
+
if (param === 0x9246) return WEBGL_RENDERER;
|
|
107
|
+
return originalGetParameter.call(this, param);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
if (typeof WebGL2RenderingContext !== "undefined") {
|
|
111
|
+
const originalGetParameter2 = WebGL2RenderingContext.prototype.getParameter;
|
|
112
|
+
WebGL2RenderingContext.prototype.getParameter = function (param) {
|
|
113
|
+
if (param === 0x9245) return WEBGL_VENDOR;
|
|
114
|
+
if (param === 0x9246) return WEBGL_RENDERER;
|
|
115
|
+
return originalGetParameter2.call(this, param);
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
} catch (_) {}
|
|
119
|
+
|
|
120
|
+
// 6. Permissions API — notifications should return 'prompt', not 'denied'
|
|
121
|
+
try {
|
|
122
|
+
const originalQuery = Permissions.prototype.query;
|
|
123
|
+
Permissions.prototype.query = function (descriptor) {
|
|
124
|
+
if (descriptor && descriptor.name === "notifications") {
|
|
125
|
+
return Promise.resolve({ state: "prompt", onchange: null });
|
|
126
|
+
}
|
|
127
|
+
return originalQuery.call(this, descriptor);
|
|
128
|
+
};
|
|
129
|
+
} catch (_) {}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
Object.defineProperty(Notification, "permission", {
|
|
133
|
+
get: () => "default",
|
|
134
|
+
configurable: true,
|
|
135
|
+
});
|
|
136
|
+
} catch (_) {}
|
|
137
|
+
|
|
138
|
+
// 7. window.chrome.runtime — ensure it exists
|
|
139
|
+
try {
|
|
140
|
+
if (!window.chrome) {
|
|
141
|
+
window.chrome = {};
|
|
142
|
+
}
|
|
143
|
+
if (!window.chrome.runtime) {
|
|
144
|
+
window.chrome.runtime = {
|
|
145
|
+
connect: function () { return {}; },
|
|
146
|
+
sendMessage: function () {},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
} catch (_) {}
|
|
150
|
+
|
|
151
|
+
// 8. Remove Chromedriver artifacts
|
|
152
|
+
try {
|
|
153
|
+
for (const prop of Object.getOwnPropertyNames(document)) {
|
|
154
|
+
if (prop.startsWith("$cdc_") || prop.startsWith("cdc_")) {
|
|
155
|
+
delete document[prop];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch (_) {}
|
|
159
|
+
|
|
160
|
+
// 9. navigator.hardwareConcurrency — ensure a reasonable value
|
|
161
|
+
try {
|
|
162
|
+
if (navigator.hardwareConcurrency < 4) {
|
|
163
|
+
Object.defineProperty(navigator, "hardwareConcurrency", {
|
|
164
|
+
get: () => 8,
|
|
165
|
+
configurable: true,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
} catch (_) {}
|
|
169
|
+
|
|
170
|
+
// 10. navigator.deviceMemory — ensure a reasonable value
|
|
171
|
+
try {
|
|
172
|
+
if (!navigator.deviceMemory || navigator.deviceMemory < 4) {
|
|
173
|
+
Object.defineProperty(navigator, "deviceMemory", {
|
|
174
|
+
get: () => 8,
|
|
175
|
+
configurable: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
} catch (_) {}
|
|
179
|
+
|
|
180
|
+
// 11. Patch iframe contentWindow.navigator to match parent
|
|
181
|
+
try {
|
|
182
|
+
const originalCreateElement = document.createElement.bind(document);
|
|
183
|
+
document.createElement = function (tagName, options) {
|
|
184
|
+
const el = originalCreateElement(tagName, options);
|
|
185
|
+
if (tagName.toLowerCase() === "iframe") {
|
|
186
|
+
el.addEventListener("load", function () {
|
|
187
|
+
try {
|
|
188
|
+
if (el.contentWindow && el.contentWindow.navigator) {
|
|
189
|
+
Object.defineProperty(el.contentWindow.navigator, "webdriver", {
|
|
190
|
+
get: () => false,
|
|
191
|
+
configurable: true,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
} catch (_) {}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return el;
|
|
198
|
+
};
|
|
199
|
+
} catch (_) {}
|
|
200
|
+
})();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
# Rootless Podman without subuid: only UID/GID 0 exist in the namespace.
|
|
4
|
+
# 1) Disable apt sandbox (apt tries to switch to _apt user → setgroups fails)
|
|
5
|
+
# 2) Wrap dpkg-statoverride so chown to non-root groups (e.g. messagebus) doesn't abort
|
|
6
|
+
# 3) Restore real dpkg-statoverride after install
|
|
7
|
+
RUN echo 'APT::Sandbox::User "root";' > /etc/apt/apt.conf.d/01disable-sandbox \
|
|
8
|
+
&& mv /usr/bin/dpkg-statoverride /usr/bin/dpkg-statoverride.real \
|
|
9
|
+
&& printf '#!/bin/sh\n/usr/bin/dpkg-statoverride.real "$@" 2>/dev/null || true\n' \
|
|
10
|
+
> /usr/bin/dpkg-statoverride && chmod +x /usr/bin/dpkg-statoverride \
|
|
11
|
+
&& apt-get update && apt-get install -y --no-install-recommends \
|
|
12
|
+
chromium xvfb ffmpeg socat curl git x11vnc xclip \
|
|
13
|
+
libegl1 libgbm1 \
|
|
14
|
+
fonts-noto-color-emoji fonts-noto-cjk \
|
|
15
|
+
&& mv /usr/bin/dpkg-statoverride.real /usr/bin/dpkg-statoverride \
|
|
16
|
+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
17
|
+
|
|
18
|
+
# noVNC + websockify for human mode (browser-based VNC client)
|
|
19
|
+
RUN git clone --depth 1 --branch v1.6.0 https://github.com/novnc/noVNC.git /opt/novnc \
|
|
20
|
+
&& git clone --depth 1 --branch v0.13.0 https://github.com/novnc/websockify.git /opt/novnc/utils/websockify
|
|
21
|
+
|
|
22
|
+
COPY --from=node:24-slim /usr/local/bin/node /usr/local/bin/node
|
|
23
|
+
COPY --from=node:24-slim /usr/local/lib/node_modules /usr/local/lib/node_modules
|
|
24
|
+
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
|
|
25
|
+
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
|
|
26
|
+
|
|
27
|
+
# Pin openclaw version — patched below for #47879 (--autoConnect → --browserUrl)
|
|
28
|
+
RUN PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install -g openclaw@2026.3.13
|
|
29
|
+
|
|
30
|
+
# Patch: replace --autoConnect with --browserUrl http://127.0.0.1:9222 in all dist files
|
|
31
|
+
# See https://github.com/openclaw/openclaw/issues/47879
|
|
32
|
+
RUN find /usr/local/lib/node_modules/openclaw/dist -name '*.js' -exec \
|
|
33
|
+
sed -i 's/"--autoConnect"/"--browserUrl","http:\/\/127.0.0.1:9222"/g' {} +
|
|
34
|
+
|
|
35
|
+
COPY --from=ghcr.io/astral-sh/uv:0.11.6 /uv /usr/local/bin/uv
|
|
36
|
+
|
|
37
|
+
WORKDIR /app
|
|
38
|
+
COPY extension-server/ ./extension-server/
|
|
39
|
+
RUN cd extension-server && UV_PYTHON_PREFERENCE=only-system uv sync
|
|
40
|
+
|
|
41
|
+
COPY chrome-extension/ ./chrome-extension/
|
|
42
|
+
|
|
43
|
+
COPY setup-openclaw.sh /setup-openclaw.sh
|
|
44
|
+
RUN chmod +x /setup-openclaw.sh
|
|
45
|
+
|
|
46
|
+
COPY entrypoint.sh /entrypoint.sh
|
|
47
|
+
RUN chmod +x /entrypoint.sh
|
|
48
|
+
|
|
49
|
+
EXPOSE 6080 7878 9223
|
|
50
|
+
|
|
51
|
+
ENTRYPOINT ["/entrypoint.sh"]
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Ensure /data exists for recording output and diagnostic logs
|
|
5
|
+
mkdir -p /data
|
|
6
|
+
|
|
7
|
+
# Start virtual display
|
|
8
|
+
Xvfb :99 -screen 0 1920x1080x24 &
|
|
9
|
+
export DISPLAY=:99
|
|
10
|
+
sleep 1
|
|
11
|
+
|
|
12
|
+
# Start the server
|
|
13
|
+
cd /app/extension-server
|
|
14
|
+
uv run uvicorn server:app --host 0.0.0.0 --port 7878 &
|
|
15
|
+
sleep 1
|
|
16
|
+
|
|
17
|
+
# Start Chrome with extension (realistic profile to reduce bot detection)
|
|
18
|
+
mkdir -p /tmp/chrome-profile/Default
|
|
19
|
+
|
|
20
|
+
cat > /tmp/chrome-profile/Default/Preferences <<'PREFS'
|
|
21
|
+
{
|
|
22
|
+
"credentials_enable_service": false,
|
|
23
|
+
"profile": {
|
|
24
|
+
"password_manager_enabled": false,
|
|
25
|
+
"password_manager_leak_detection": false,
|
|
26
|
+
"name": "Default",
|
|
27
|
+
"created_by_version": "131.0.6778.139",
|
|
28
|
+
"content_settings": {
|
|
29
|
+
"exceptions": {}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"browser": {
|
|
33
|
+
"has_seen_welcome_page": true,
|
|
34
|
+
"check_default_browser": false,
|
|
35
|
+
"window_placement": {
|
|
36
|
+
"bottom": 1080,
|
|
37
|
+
"left": 0,
|
|
38
|
+
"maximized": false,
|
|
39
|
+
"right": 1920,
|
|
40
|
+
"top": 0,
|
|
41
|
+
"work_area_bottom": 1080,
|
|
42
|
+
"work_area_left": 0,
|
|
43
|
+
"work_area_right": 1920,
|
|
44
|
+
"work_area_top": 0
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"dns_prefetching": {
|
|
48
|
+
"enabled": true
|
|
49
|
+
},
|
|
50
|
+
"safebrowsing": {
|
|
51
|
+
"enabled": true
|
|
52
|
+
},
|
|
53
|
+
"search": {
|
|
54
|
+
"suggest_enabled": true
|
|
55
|
+
},
|
|
56
|
+
"translate": {
|
|
57
|
+
"enabled": false
|
|
58
|
+
},
|
|
59
|
+
"intl": {
|
|
60
|
+
"accept_languages": "en-US,en"
|
|
61
|
+
},
|
|
62
|
+
"distribution": {
|
|
63
|
+
"import_bookmarks": false,
|
|
64
|
+
"skip_first_run_ui": true
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
PREFS
|
|
68
|
+
|
|
69
|
+
cat > /tmp/chrome-profile/Default/Bookmarks <<'BOOKMARKS'
|
|
70
|
+
{
|
|
71
|
+
"checksum": "b5f7e1a2c3d4e5f6a7b8c9d0e1f2a3b4",
|
|
72
|
+
"roots": {
|
|
73
|
+
"bookmark_bar": {
|
|
74
|
+
"children": [
|
|
75
|
+
{
|
|
76
|
+
"date_added": "13350000000000000",
|
|
77
|
+
"date_last_used": "0",
|
|
78
|
+
"guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
79
|
+
"name": "Google",
|
|
80
|
+
"type": "url",
|
|
81
|
+
"url": "https://www.google.com/"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"date_added": "13350000000000000",
|
|
85
|
+
"date_last_used": "0",
|
|
86
|
+
"guid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
|
|
87
|
+
"name": "YouTube",
|
|
88
|
+
"type": "url",
|
|
89
|
+
"url": "https://www.youtube.com/"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"date_added": "13350000000000000",
|
|
93
|
+
"date_last_used": "0",
|
|
94
|
+
"guid": "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
|
95
|
+
"name": "Wikipedia",
|
|
96
|
+
"type": "url",
|
|
97
|
+
"url": "https://en.wikipedia.org/"
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"date_added": "13350000000000000",
|
|
101
|
+
"date_last_used": "0",
|
|
102
|
+
"date_modified": "13350000000000000",
|
|
103
|
+
"guid": "00000000-0000-4000-a000-000000000001",
|
|
104
|
+
"name": "Bookmarks bar",
|
|
105
|
+
"type": "folder"
|
|
106
|
+
},
|
|
107
|
+
"other": {
|
|
108
|
+
"children": [],
|
|
109
|
+
"date_added": "13350000000000000",
|
|
110
|
+
"date_last_used": "0",
|
|
111
|
+
"date_modified": "0",
|
|
112
|
+
"guid": "00000000-0000-4000-a000-000000000002",
|
|
113
|
+
"name": "Other bookmarks",
|
|
114
|
+
"type": "folder"
|
|
115
|
+
},
|
|
116
|
+
"synced": {
|
|
117
|
+
"children": [],
|
|
118
|
+
"date_added": "13350000000000000",
|
|
119
|
+
"date_last_used": "0",
|
|
120
|
+
"date_modified": "0",
|
|
121
|
+
"guid": "00000000-0000-4000-a000-000000000003",
|
|
122
|
+
"name": "Mobile bookmarks",
|
|
123
|
+
"type": "folder"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"version": 1
|
|
127
|
+
}
|
|
128
|
+
BOOKMARKS
|
|
129
|
+
|
|
130
|
+
cat > /tmp/chrome-profile/'Local State' <<'LOCALSTATE'
|
|
131
|
+
{
|
|
132
|
+
"browser": {
|
|
133
|
+
"enabled_labs_experiments": []
|
|
134
|
+
},
|
|
135
|
+
"profile": {
|
|
136
|
+
"info_cache": {
|
|
137
|
+
"Default": {
|
|
138
|
+
"active_time": 1710000000,
|
|
139
|
+
"is_consented_primary_account": false,
|
|
140
|
+
"name": "Person 1"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"user_experience_metrics": {
|
|
145
|
+
"reporting_enabled": false
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
LOCALSTATE
|
|
149
|
+
|
|
150
|
+
chromium \
|
|
151
|
+
--window-size=1920,1080 \
|
|
152
|
+
--window-position=0,0 \
|
|
153
|
+
--no-first-run \
|
|
154
|
+
--disable-default-apps \
|
|
155
|
+
--no-sandbox \
|
|
156
|
+
--disable-infobars \
|
|
157
|
+
--disable-dev-shm-usage \
|
|
158
|
+
--disable-blink-features=AutomationControlled \
|
|
159
|
+
--use-gl=angle --use-angle=swiftshader \
|
|
160
|
+
--enable-unsafe-swiftshader \
|
|
161
|
+
--enable-webgl \
|
|
162
|
+
--password-store=basic \
|
|
163
|
+
--use-mock-keychain \
|
|
164
|
+
--disable-sync \
|
|
165
|
+
--disable-features=PasswordLeakDetection,PasswordManager \
|
|
166
|
+
--user-data-dir=/tmp/chrome-profile \
|
|
167
|
+
--remote-debugging-port=9222 \
|
|
168
|
+
--remote-debugging-address=127.0.0.1 \
|
|
169
|
+
--remote-allow-origins=* \
|
|
170
|
+
--load-extension=/app/chrome-extension \
|
|
171
|
+
--disable-extensions-except=/app/chrome-extension \
|
|
172
|
+
about:blank &
|
|
173
|
+
|
|
174
|
+
# Forward CDP port to all interfaces
|
|
175
|
+
sleep 2
|
|
176
|
+
socat TCP-LISTEN:9223,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:9222 &
|
|
177
|
+
|
|
178
|
+
# Always start noVNC so users can watch the browser in any mode.
|
|
179
|
+
# In human mode x11vnc also fires disconnect hooks for the watchdog.
|
|
180
|
+
echo "Starting noVNC..."
|
|
181
|
+
VNC_GONE_HOOK=""
|
|
182
|
+
VNC_ACCEPT_HOOK=""
|
|
183
|
+
if [ "$HUMAN_MODE" = "1" ]; then
|
|
184
|
+
VNC_GONE_HOOK="-gone touch /data/.vnc-disconnected"
|
|
185
|
+
VNC_ACCEPT_HOOK="-afteraccept rm -f /data/.vnc-disconnected"
|
|
186
|
+
fi
|
|
187
|
+
x11vnc -display :99 -nopw -shared -forever -rfbport 5900 -xkb \
|
|
188
|
+
$VNC_GONE_HOOK $VNC_ACCEPT_HOOK &
|
|
189
|
+
sleep 1
|
|
190
|
+
|
|
191
|
+
/opt/novnc/utils/novnc_proxy --vnc localhost:5900 --listen 6080 &
|
|
192
|
+
sleep 1
|
|
193
|
+
echo "============================================"
|
|
194
|
+
echo "noVNC ready: http://localhost:6080/vnc.html"
|
|
195
|
+
echo "============================================"
|
|
196
|
+
|
|
197
|
+
# Human mode: wait for VNC disconnect or eval match
|
|
198
|
+
if [ "$HUMAN_MODE" = "1" ]; then
|
|
199
|
+
echo "Human mode active."
|
|
200
|
+
if [ -n "$INSTRUCTION" ]; then
|
|
201
|
+
echo ""
|
|
202
|
+
echo "TASK: $INSTRUCTION"
|
|
203
|
+
echo ""
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# Human watchdog: no idle detection (humans pause to think)
|
|
207
|
+
MAX_WAIT=${TIME_LIMIT_S:-1800}
|
|
208
|
+
ELAPSED=0
|
|
209
|
+
DISCONNECT_WAIT=0
|
|
210
|
+
STOP_REASON=""
|
|
211
|
+
|
|
212
|
+
while [ "$ELAPSED" -lt "$MAX_WAIT" ]; do
|
|
213
|
+
sleep 5
|
|
214
|
+
ELAPSED=$((ELAPSED + 5))
|
|
215
|
+
|
|
216
|
+
# Check if eval interceptor matched
|
|
217
|
+
if [ -f /data/.stop-requested ]; then
|
|
218
|
+
echo "Stop requested by server (eval matched)."
|
|
219
|
+
STOP_REASON="eval_matched"
|
|
220
|
+
break
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Check if VNC client disconnected (with 15s grace period for reconnect)
|
|
224
|
+
if [ -f /data/.vnc-disconnected ]; then
|
|
225
|
+
DISCONNECT_WAIT=$((DISCONNECT_WAIT + 5))
|
|
226
|
+
if [ "$DISCONNECT_WAIT" -ge 15 ]; then
|
|
227
|
+
echo "VNC client disconnected for ${DISCONNECT_WAIT}s, assuming done."
|
|
228
|
+
STOP_REASON="vnc_disconnected"
|
|
229
|
+
break
|
|
230
|
+
fi
|
|
231
|
+
else
|
|
232
|
+
DISCONNECT_WAIT=0
|
|
233
|
+
fi
|
|
234
|
+
done
|
|
235
|
+
|
|
236
|
+
if [ -z "$STOP_REASON" ]; then
|
|
237
|
+
echo "Time limit (${MAX_WAIT}s) exceeded."
|
|
238
|
+
STOP_REASON="time_limit_exceeded"
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
echo "$STOP_REASON" > /data/.stop-reason
|
|
242
|
+
|
|
243
|
+
# Finalize bookkeeping (eval promotion, etc.) — recording keeps running
|
|
244
|
+
curl -sf -X POST http://localhost:7878/api/stop || true
|
|
245
|
+
rm -f /data/.stop-requested /data/.vnc-disconnected
|
|
246
|
+
|
|
247
|
+
# Grace period: keep recording for 15s to capture end state
|
|
248
|
+
echo "Human finished, recording grace period (15s)..."
|
|
249
|
+
sleep 15
|
|
250
|
+
|
|
251
|
+
# Stop recording
|
|
252
|
+
echo "Stopping recording..."
|
|
253
|
+
curl -sf -X POST http://localhost:7878/api/stop-recording || true
|
|
254
|
+
sleep 2
|
|
255
|
+
echo "Done."
|
|
256
|
+
exit 0
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
# If no INSTRUCTION provided, skip OpenClaw and keep container alive for external use
|
|
260
|
+
if [ -z "$INSTRUCTION" ]; then
|
|
261
|
+
echo "No INSTRUCTION set, running in manual mode (no OpenClaw agent)."
|
|
262
|
+
wait -n
|
|
263
|
+
exit 0
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
# Generate a shared gateway token so gateway and agent can authenticate
|
|
267
|
+
OPENCLAW_GATEWAY_TOKEN="$(head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n')"
|
|
268
|
+
export OPENCLAW_GATEWAY_TOKEN
|
|
269
|
+
|
|
270
|
+
# Generate OpenClaw config from env vars
|
|
271
|
+
/setup-openclaw.sh
|
|
272
|
+
|
|
273
|
+
# Copy /my-info/ into the OpenClaw workspace so the agent can access it via ./my-info/
|
|
274
|
+
WORKSPACE=/root/workspace
|
|
275
|
+
mkdir -p "$WORKSPACE"
|
|
276
|
+
if [ -d /my-info ]; then
|
|
277
|
+
cp -r /my-info "$WORKSPACE/my-info"
|
|
278
|
+
echo "Copied /my-info to $WORKSPACE/my-info"
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
# Wait for Chrome CDP to be ready
|
|
282
|
+
echo "Waiting for Chrome CDP..."
|
|
283
|
+
for i in $(seq 1 30); do
|
|
284
|
+
if curl -sf http://127.0.0.1:9222/json/version > /dev/null 2>&1; then
|
|
285
|
+
echo "Chrome CDP ready"
|
|
286
|
+
break
|
|
287
|
+
fi
|
|
288
|
+
if [ "$i" -eq 30 ]; then
|
|
289
|
+
echo "Chrome CDP not ready after 30s, aborting"
|
|
290
|
+
echo "chrome_cdp_timeout" > /data/.stop-reason
|
|
291
|
+
exit 1
|
|
292
|
+
fi
|
|
293
|
+
sleep 1
|
|
294
|
+
done
|
|
295
|
+
|
|
296
|
+
# Start OpenClaw gateway (log to /data for post-mortem debugging)
|
|
297
|
+
openclaw gateway run > /data/gateway.log 2>&1 &
|
|
298
|
+
GATEWAY_PID=$!
|
|
299
|
+
sleep 3
|
|
300
|
+
|
|
301
|
+
# Check gateway is alive
|
|
302
|
+
if ! kill -0 $GATEWAY_PID 2>/dev/null; then
|
|
303
|
+
echo "ERROR: OpenClaw gateway died on startup. Log:"
|
|
304
|
+
cat /data/gateway.log
|
|
305
|
+
echo "gateway_failed" > /data/.stop-reason
|
|
306
|
+
exit 1
|
|
307
|
+
fi
|
|
308
|
+
echo "OpenClaw gateway running (pid=$GATEWAY_PID)"
|
|
309
|
+
|
|
310
|
+
# Run the agent from workspace directory (where my-info/ lives)
|
|
311
|
+
echo "Starting OpenClaw agent from $WORKSPACE..."
|
|
312
|
+
TIMEOUT_MS=$(( ${TIME_LIMIT_S:-1800} * 1000 ))
|
|
313
|
+
AGENT_CMD=(openclaw agent --session-id clawbench --message "$INSTRUCTION" --thinking "${THINKING_LEVEL:-medium}" --timeout "$TIMEOUT_MS" --local)
|
|
314
|
+
echo "Agent command: ${AGENT_CMD[*]}"
|
|
315
|
+
if [ -n "$TEMPERATURE" ]; then
|
|
316
|
+
AGENT_CMD+=(--temperature "$TEMPERATURE")
|
|
317
|
+
fi
|
|
318
|
+
if [ -n "$MAX_TOKENS" ]; then
|
|
319
|
+
AGENT_CMD+=(--max-tokens "$MAX_TOKENS")
|
|
320
|
+
fi
|
|
321
|
+
cd "$WORKSPACE"
|
|
322
|
+
"${AGENT_CMD[@]}" > /data/agent.log 2>&1 &
|
|
323
|
+
AGENT_PID=$!
|
|
324
|
+
|
|
325
|
+
# Watchdog: wait for agent activity, then detect idle (no new actions for 300s)
|
|
326
|
+
IDLE_THRESHOLD=300
|
|
327
|
+
MAX_WAIT=${TIME_LIMIT_S:-1800}
|
|
328
|
+
ELAPSED=0
|
|
329
|
+
LAST_SIZE=0
|
|
330
|
+
IDLE=0
|
|
331
|
+
STOP_REASON=""
|
|
332
|
+
|
|
333
|
+
while kill -0 $AGENT_PID 2>/dev/null && [ "$ELAPSED" -lt "$MAX_WAIT" ]; do
|
|
334
|
+
sleep 5
|
|
335
|
+
ELAPSED=$((ELAPSED + 5))
|
|
336
|
+
|
|
337
|
+
# Check if server requested stop (eval interceptor matched)
|
|
338
|
+
if [ -f /data/.stop-requested ]; then
|
|
339
|
+
echo "Stop requested by server (eval matched), killing agent."
|
|
340
|
+
STOP_REASON="eval_matched"
|
|
341
|
+
break
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
CURRENT_SIZE=$(wc -c < /data/actions.jsonl 2>/dev/null || echo 0)
|
|
345
|
+
|
|
346
|
+
if [ "$CURRENT_SIZE" -gt 0 ] && [ "$CURRENT_SIZE" -eq "$LAST_SIZE" ]; then
|
|
347
|
+
IDLE=$((IDLE + 5))
|
|
348
|
+
if [ "$IDLE" -ge "$IDLE_THRESHOLD" ]; then
|
|
349
|
+
echo "Agent idle for ${IDLE_THRESHOLD}s, assuming done."
|
|
350
|
+
STOP_REASON="agent_idle"
|
|
351
|
+
break
|
|
352
|
+
fi
|
|
353
|
+
else
|
|
354
|
+
IDLE=0
|
|
355
|
+
fi
|
|
356
|
+
LAST_SIZE=$CURRENT_SIZE
|
|
357
|
+
done
|
|
358
|
+
|
|
359
|
+
# Determine stop reason if not set (loop exited without breaking)
|
|
360
|
+
if [ -z "$STOP_REASON" ]; then
|
|
361
|
+
if ! kill -0 $AGENT_PID 2>/dev/null; then
|
|
362
|
+
STOP_REASON="agent_exited"
|
|
363
|
+
else
|
|
364
|
+
echo "Time limit (${MAX_WAIT}s) exceeded, killing agent."
|
|
365
|
+
STOP_REASON="time_limit_exceeded"
|
|
366
|
+
fi
|
|
367
|
+
fi
|
|
368
|
+
|
|
369
|
+
echo "$STOP_REASON" > /data/.stop-reason
|
|
370
|
+
|
|
371
|
+
# Kill all openclaw processes
|
|
372
|
+
kill $AGENT_PID 2>/dev/null || true
|
|
373
|
+
kill $GATEWAY_PID 2>/dev/null || true
|
|
374
|
+
pkill -f "openclaw" 2>/dev/null || true
|
|
375
|
+
sleep 2
|
|
376
|
+
|
|
377
|
+
# Copy OpenClaw session transcript to /data
|
|
378
|
+
cp /root/.openclaw/agents/main/sessions/clawbench.jsonl /data/agent-messages.jsonl 2>/dev/null || true
|
|
379
|
+
|
|
380
|
+
# Finalize bookkeeping (eval promotion, etc.) — recording keeps running
|
|
381
|
+
curl -sf -X POST http://localhost:7878/api/stop || true
|
|
382
|
+
|
|
383
|
+
# Clean up internal marker (created by /api/stop)
|
|
384
|
+
rm -f /data/.stop-requested
|
|
385
|
+
|
|
386
|
+
# Grace period: keep recording for 15s after agent is killed to capture end result
|
|
387
|
+
echo "Agent finished, recording grace period (15s)..."
|
|
388
|
+
sleep 15
|
|
389
|
+
|
|
390
|
+
# Stop recording
|
|
391
|
+
echo "Stopping recording..."
|
|
392
|
+
curl -sf -X POST http://localhost:7878/api/stop-recording || true
|
|
393
|
+
sleep 2
|
|
394
|
+
echo "Done."
|