尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗מתכנן טיולים בעברית — בונים מסלול יום-אחר-יום, מקבלים קוד שיתוף בן 6 ספרות, ושולחים לחברים.
public/js/popular.js, רשימה אחת לדף הבית ולאשף) — וקרוסלת טיולים מומלציםbuild_now של המודל נזרק בקוד). ה-AI בונה ועורך את התחנות בעצמו דרך אותם ops, אותו אימות מקומות ואותו כותב של השינוי החכם, ומעבר לתחנות — מלונות, מסגרת התקציב ותאריך היציאה (lib/plan-actions.js, עם היפוך מדויק לכל פעולה). כל שינוי הוא כרטיס אחד, וה-AI מחליט בעצמו בכל תור אם הוא מבצע מיד (עם ביטול) — שינוי שהתבקש במילים, או עובדה שנמסרה: כרטיס, הזמנה, תקציב — או מציע לאישור — יוזמה שלו, מחיקה או החלפה של מה שהמטיילים בחרו. (היה כאן מתג ידני/אוטומטי; הנתונים הראו אותו נלחץ הלוך ושוב.) ליד שורת ההקלדה + לצירוף כרטיס טיסה, אישור מלון או צילום מסך: הקובץ נקרא במכשיר (שכבת הטקסט של PDF, OCR של הדפדפן או tesseract — אותו סולם כמו במסמכים, doc-text.js), ולשרת נשלח הטקסט ולא הקובץ; רק תמונה שהמכשיר לא הצליח לקרוא נשלחת כעותק מוקטן (עד 1024px, JPEG) שהמודל קורא ברמת low — עלות קבועה וזעירה. עד 3 קבצים ביום למשתמש (lib/attachments.js, מונה files), וה-AI פותח את התשובה במה שהבין מהקובץ ("ראיתי שהעליתם כרטיסי טיסה עם קונקשן בציריך") ואז ממלא: טיסה למסלול, מלון לרשימת המלונות, תאריך לטיול. כל קובץ שמצורף נשמר ברקע גם במסמכים של הטיול (TripDocs.stash), מתויג לפי הטקסט שכבר נקרא — בלי OCR שני ובלי הודעה; התג על טאב המסמכים הוא כל ההכרזה. חלוקת האזורים שה-AI מציע מצוירת על לוח שנה בתוך הצ'אט, וצובעים ימים כדי לשנות. כשהטיול נראה מגובש ה-AI מציע פעם אחת להזמין חברים לתכנון (וואטסאפ / קישור). כל תור על המודל החזק — צ'אט הוא קול אחד, ושני מודלים נשמעו כשניים. השיחה וזיכרון התכנון (trips.plan_state: שלב, עובדות שהוסכמו, חלוקת האזורים) נשמרים בשרת (trip_plan_messages), כך שהמודל לא "שוכח" החלטה מלפני עשרים הודעות. מרגע השמירה הטאב הופך להמלצות. routes/plan.js; 20 הודעות ליום למשתמשPOST /api/trips/code/:code/recs: המודל החזק עונה, כל מקום מאומת מול Photon/Google ומה שלא אומת נזרק, תמונות מוויקיפדיה (חינם, בלי מפתח), 30 שאלות ליום למשתמשtrip_day_recs) כך שכל המשתתפים רואים אותן, ונטענות לפי הסדר הכרונולוגי ובבקשות קטנות: קודם נקבעת חלוקת הימים לאזורים — החלוקה שהמטיילים קבעו בצ'אט התכנון אם יש כזו, אחרת זו שהתחנות כבר מכתיבות, ורק כשאין אף אחת מהן בקשה נפרדת ל-AI (trip_rec_areas, נשמרת ונשאלת מחדש אחרי כל שינוי; היא פנימית בלבד ולא משנה את המסלול) — ואז בקשה אחת לכל אזור, עד שישה ימים בכל בקשה, עם הימים הקבועים שבטווח כ-context. כל יום נכתב ברגע שהוא מוכן, כך שטיול של 30 יום מתמלא מיום 1 והלאה מול המשתמש במקום להמתין לסוף; הטעינה מתחילה כבר במסך הטעינה של הדף ושל הבנייה. אחרי כל שינוי במסלול — מהצ'אטים או ידני — נשלחת בקשה נוספת, שמשחזרת רק ימים שה-context שלהם השתנה (האזור והתחנות משני צדדיו), כך שתחנה שנוספה ביום 3 לא מייצרת 30 קריאות מודל. routes/day-recs.js, public/js/trip-dayrecs.js; בדיקת המבנה בלי מודל: node tools/day-recs-plan-check.js; הכפתור מוגבל ל-40 רענונים ליום למשתמש🚪 סגור, עם התגית שממנה זה נקבע). המקור החינמי קודם: התגית opening_hours של OSM דרך ה-endpoint של האלמנט (בלי מפתח, בלי מכסה), וגוגל רק למה ש-OSM לא ידע. אף פעם לא מנחשים: תגית שהפרסר לא מבין במלואה מקבלת unknown ולא "סגור", כי סימון שגוי גורם למטייל לוותר על מקום פתוח. הסימון הוא הצעה בלבד — שום דבר לא זז ולא נמחק מהמסלול. lib/opening-hours.js, routes/hours.js; בדיקה בלי רשת: node tools/opening-hours-check.js; בדיקת נגישות המקורות מהשרת: node tools/opening-hours-probe.js; מדידת הבסיס על טיולים אמיתיים: node tools/opening-hours-audit.jslib/hours-fit.js). המלצות ליום פנוי פשוט לא מציעות מקום שסגור באותו יום. הדיווח נשאר בכל מקרה — תחנה שנוספה ידנית או שעה ששונתה ביד לא עוברות כאן כלל⏱ לא מספיקים; לעורך הסימון הוא התיקון: לחיצה אחת מזיזה אותה לשעה הראשונה שאפשר להגיע בה). הכלל אחד, משותף לבנייה ולדף (public/js/travel-rules.js): לכל קטגוריה רצפת שהייה (ארוחה 45 דק׳, מוזיאון שעה…) — לא הערכה, מינימום — ותחנה נחשבת מחוץ להישג רק כשגם המינימום ועוד הדרך לא נכנסים בפער. קטע של מעל 120 ק״מ לא נשפט (רכבת? מטוס?), וגם קטע אל תחנת נסיעה או ממנה — היא עצמה הנסיעה. בבנייה (lib/travel-fit.js, לפני בדיקת השעות) תחנה כזו זזה קדימה, בתוך היום שלה, ולעולם לא מעבר לתחנה הבאה — אחרת מדווחת ולא זזה. תשובות OSRM נשמרות ב-api_cache, ורוּטר שלא עונה לא משנה כלום. בדיקה בלי רשת: node tools/travel-fit-check.jspublic/js/doc-classify.js, ובדיקתם: node tools/docs-classify-check.jsroutes/checklist.js, public/js/trip-checklist.js, המילון המשותף ב-public/js/checklist-tags.js; טבלאות trip_checklists, trip_checklist_items, trip_checklist_tickslib/quota.js) — מונה יומי אחד לכל פיצ'ר (בניות, שינויים חכמים, שאלות המלצות, הודעות תכנון), נשמר ב-DB (ai_usage) ולא בזיכרון, כי השרת החינמי מתאתחל בכל deploy ונרדם אחרי רבע שעה. מנהלים לא מוגבלים. כשהמכסה נגמרת הצ'אט לא מציג שגיאה אלא כרטיס: אפשר להמשיך ידנית ומחר יש מכסה חדשה — ומי שמשתף את האפליקציה בוואטסאפ ושלושה חברים נרשמים דרך הקישור שלו (/?ref=CODE, GET /api/me/referral) מקבל מכסה כפולה לכל המונים, לתמיד/trip/CODE#plan)lib/guest.js: שורת users בלי אימייל, JWT עם guest: true), והאשף, צ'אט התכנון, הבנייה והעריכה עובדים לו כמו לכל אחד. החומה עומדת איפה שהטיול הופך לאמיתי לאנשים אחרים — שמירת הטיול (חשיפת קוד השיתוף) והצטרפות לטיול של מישהו אחר — ושם ההרשמה שומרת הכול: הרשמה הופכת את שורת האורח עצמה לחשבון (אותו id), והתחברות לחשבון קיים מעבירה אליו את הטיולים, ההצטרפויות וההצבעות ומוחקת את האורח (גם דרך Google, עם ?guest= על הקישור). "הטיולים שלי" אומר לאורח שהטיולים שמורים בדפדפן הזה בלבד. טביעת אורחים מוגבלת ל-10 לרבע שעה לכתובת, כמו הרשמה — כל אורח הוא מכסות AI טריותapi/index.js), PostgreSQL (Neon, דרך Vercel)האתר מצהיר על עצמו לסוכנים דרך WebMCP: במקום שסוכן ינחש איפה לוחצים, הדף מגיש לו כלים בשם, עם סכימת קלט ותוצאה מובנית.
הכלים נרשמים לכל מארח שהדפדפן חושף, לפי הסדר: document.modelContext (הטיוטה של W3C),
navigator.modelContext (Edge 147 / ה-origin trial של Chrome 149), ובכל מקרה גם window.tripmakerTools —
גשר משלנו. הגשר הוא זה שעובד היום בפועל: תמיכה נייטיבית עדיין דקה, ורוב הסוכנים שמגיעים לאתר עושים זאת
דרך דפדפן headless שבו הערוץ היחיד הוא הרצת JS.
window.tripmakerTools.host // 'document' | 'navigator' | null
window.tripmakerTools.list() // כל הכלים: שם, תיאור, inputSchema, annotations
await window.tripmakerTools.call(name, input)
התוצאה תמיד באותו מבנה — content קריא לאדם ו-structuredContent לפרסור. כישלון חוזר כתוצאה עם
isError: true ולא כחריגה, כדי שסוכן יוכל לקרוא את הסיבה ולנסות מהלך אחר.
בכל דף (webmcp-tools.js): tripmaker_whoami · tripmaker_search_trips ·
tripmaker_list_suggested_trips · tripmaker_get_trip · tripmaker_list_my_trips ·
tripmaker_search_destinations · tripmaker_create_trip · tripmaker_open
רק כשטיול פתוח (נרשמים מ-trip.js): tripmaker_current_trip · tripmaker_add_stop ·
tripmaker_update_stop · tripmaker_delete_stop
כלי התחנות עוברים דרך addItem/saveItem/deleteItem של הדף עצמו, לא ישר ל-API — כך עריכה של סוכן
מתרנדרת בשתי התצוגות בדיוק כמו עריכה של אדם, במקום להשאיר על המסך מסלול שכבר לא קיים.
הערות אבטחה: כלי קריאה מסומנים readOnlyHint, וכל כלי שמחזיר תוכן שנוצר על ידי משתמשים אחרים מסומן גם
untrustedContentHint. הרשאות לא נעקפות — הכלים רוכבים על אותו JWT ואותן בדיקות הרשאה של ה-API, וסוכן
לא מתחבר בשם המשתמש. /admin לא טוען את הכלים בכוונה.
node db/migrate.js — עמודת destinations (ריבוי יעדים + קואורדינטות), אידמפוטנטי.
npm install
# צרו קובץ .env עם:
# DATABASE_URL=postgresql://...
node db/setup.js # פעם אחת: סכימה + טיולים מומלצים
npm start # http://localhost:3000
npm test # כל הבדיקות שלא צריכות רשת, מפתח או DB: תחביר של כל קובץ + ארבעת סקריפטי ה-check
| משתנה | תיאור |
|---|---|
DATABASE_URL | כתובת חיבור ל-PostgreSQL. הסכימה, המיגרציות והטיולים המומלצים נוצרים לבד בעלייה (lib/db-bootstrap.js) — מסד נתונים חדש לא צריך שום צעד ידני |
IMPORT_FROM_DATABASE_URL | חד-פעמי: כתובת מסד הנתונים הישן (חי). בעלייה הבאה השרת מעתיק ממנו את כל הטבלאות (משתמשים, טיולים, תחנות…) עם אותם מזהים — רק לתוך מסד שאין בו עדיין משתמשים, אחרת מדלג. מסירים אחרי הריצה |
DB_BACKUP_KEY | סוד לפענוח גיבוי ה-escrow החודשי. כשהוא מוגדר, מסד ריק (או שיש בו רק טיולי הדגמה) משוחזר אוטומטית בעלייה מהגיבוי המוצפן ב-GitHub (lib/db-restore.js). בלעדיו אין שחזור — רק seed |
RESTORE_FROM_ESCROW | 1 = לכפות שחזור מה-escrow גם על טיולי הדגמה שכבר קיימים (התאוששות חד-פעמית). מוחק ומשחזר; מסירים אחרי הריצה. בלי זה השחזור רץ רק על מסד ריק/דגמה |
RESTORE_SOURCE_URL | אופציונלי — כתובת קובץ ה-escrow. ברירת מחדל: ה-raw של ofir6868/tripi בענף db-rotation-backups |
JWT_SECRET | סוד לחתימת טוקנים |
PORT | פורט (ברירת מחדל 3000). רלוונטי רק כשמריצים את server.js ישירות — על Vercel אין פורט להאזין עליו |
CRON_SECRET | הטוקן שהמתזמן של Vercel חותם איתו את /api/cron/tick — התזכורות, גיבוי ה-escrow וטאטוא המטמון, שעל מארח תמיד-דלוק רצים כטיימרים בתוך התהליך. בלעדיו הנתיב מסרב לכולם, וזו התשובה הנכונה לנתיב שאחרת היה נותן לזר לשרוף לנו את מכסת ה-push |
OSRM_URL | אופציונלי — שרת הניתוב לזמני הנסיעה בין תחנות בבנייה (lib/travel-fit.js). ברירת מחדל: השרת הציבורי https://router.project-osrm.org, בלי מפתח. הדפדפן פונה אליו ישירות ולא דרך המשתנה הזה |
CANONICAL_ORIGIN | אופציונלי — המקור היחיד שמותר להיכנס לאינדקס. ברירת מחדל https://tripmaker.co.il (מתוך SITE ב-lib/seo.js). כל בקשת GET לדף שמגיעה למארח או לסכימה אחרים מקבלת 301 לכאן |
GOOGLE_PLACES_API_KEY | מפתח Google Places (New) — אוטוקומפליט של יעדים, וגם תמונות המקומות בכרטיסי ההמלצות. בלעדיו התמונות נופלות חזרה לוויקיפדיה |
AI_MODEL_PLAN | אופציונלי — המודל של צ'אט התכנון. ברירת מחדל: , כי התורות שלו מכניסות מקומות אמיתיים ישר למסלול |
ב-Google Cloud → OAuth client (Web application):
https://tripmaker.co.il (ו־http://localhost:3000 לפיתוח)https://tripmaker.co.il/api/auth/google/callback (ו־http://localhost:3000/api/auth/google/callback)אחרי הגדרת המשתנים, הריצו node db/migrate.js פעם אחת (עמודת google_sub + סיסמה אופציונלית).
בדיקת הספק הפעיל: node tools/hotel-content-probe.js (או node tools/hotel-content-probe.js liteapi).
AI_MODEL_STRONGAI_QUOTA_PLAN / AI_QUOTA_RECS / AI_QUOTA_BUILD / AI_QUOTA_EDIT / AI_QUOTA_CHECKLIST | אופציונלי — מכסה יומית למשתמש לכל מונה (ברירות מחדל 20 / 30 / 20 / 3 / 10; הצ'קליסט נספר רק ברענון, הבנייה הראשונה לטיול חינם). AI_QUOTA_PLAN=2 הוא הדרך לראות את כרטיס המכסה בלי שישים תורות אמיתיים |
PLACE_PHOTO_MONTHLY_LIMIT | תקרת הורדות התמונה מ-Google בחודש (ברירת מחדל 900 — מתחת ל-1,000 החינמיות של SKU Place Details Photos). מעבר לתקרה, תמונות מגיעות מוויקיפדיה. כל מקום נמשך פעם אחת ונשמר בקאש ב-DB (api_cache), כך שגם deploy לא קונה אותו מחדש. כל תקרות Google מוגדרות ב-lib/google-skus.js, נספרות לפי החודש של גוגל (שעון פסיפיק), משתחררות לאורך החודש, ומוצגות ב-/admin/google |
PLACE_HOURS_MONTHLY_LIMIT | תקרת בדיקות שעות הפתיחה מול Google בחודש (ברירת מחדל 900 — מתחת למכסת החינם של 1,000 בדרג Enterprise). OSM עונה חינם ובלי מכסה, וגוגל נשאל רק על מה ש-OSM לא ידע |
PLACE_SEARCH_MONTHLY_LIMIT | תקרת "חוות הדעת השנייה" של Google באימות מקומות (ברירת מחדל 4,000). מעבר לתקרה האימות נשאר על Photon בלבד — הוורדיקט אף פעם לא הופך ל-ok בגלל תקציב, הוא פשוט נשאר מה ש-OSM אמר |
PLACES_AUTOCOMPLETE_MONTHLY_LIMIT | תקרת האוטוקומפליט של בוחר המקומות (ברירת מחדל 4,000). מעבר לתקרה הבוחר נופל ל-Photon, בדיוק כמו בלי מפתח |
PLACES_DETAILS_MONTHLY_LIMIT | תקרת פענוח הקואורדינטות של מקום שנבחר (ברירת מחדל 5,000) |
GOOGLE_MONITORING_KEY | אופציונלי — JSON של service account עם התפקיד Monitoring Viewer בפרויקט של מפתח ה-Places (גולמי או ב-base64). כשמוגדר, /admin/google משווה את המונה שלנו למה שגוגל עצמו ספר. GOOGLE_MONITORING_PROJECT דורס את הפרויקט שהמפתח מציין |
BRAVE_SEARCH_API_KEY | אופציונלי — מפתח Brave Search (2,000 חיפושים חינם בחודש) לכלי החיפוש של ה-AI (lib/web-search.js). בלעדיו החיפוש עובר דרך דף ה-HTML של DuckDuckGo, בלי מפתח. בשני המקרים התוצאות מסוננות לרשימת אתרי הטיולים המורשים בלבד (Booking, Klook, SWISS, אל על, Google Maps, Tripadvisor, Wikipedia…) |
AI_SEARCH_EXTRA_SITES | אופציונלי — אתרים נוספים שמותר ל-AI לחפש ולקרוא בהם, מופרדים בפסיקים (example.com,other.org) |
CONNECTOR_SECRET | אופציונלי — מפתח ההצפנה של טוקני החיבורים (יומן/Gmail/Drive) ב-user_connectors. ברירת מחדל: נגזר מ-JWT_SECRET. החלפתו מנתקת את כל החיבורים (השורות הופכות בלתי-קריאות ונחשבות מנותקות) |
CONNECTORS_REDIRECT_URI | אופציונלי — ברירת מחדל {origin}/api/connectors/google/callback. צריך להיות רשום ב-Google Cloud לצד ה-redirect של ההתחברות, ואותו Client ID משמש את שניהם. ה-scopes: calendar.readonly, gmail.readonly, drive.readonly — קריאה בלבד |
GOOGLE_CLIENT_ID | OAuth 2.0 Client ID מ-Google Cloud (התחברות, וגם החיבורים של ה-AI) |
GOOGLE_CLIENT_SECRET | OAuth 2.0 Client Secret מ-Google Cloud |
GOOGLE_REDIRECT_URI | אופציונלי — ברירת מחדל {origin}/api/auth/google/callback (בפרודקשן: https://tripmaker.co.il/api/auth/google/callback) |
HOTEL_CONTENT_PROVIDER | ספק תוכן המלונות: osm (ברירת מחדל, ללא מפתח) או liteapi |
LITEAPI_KEY | מפתח LiteAPI — בלעדיו המערכת נופלת חזרה ל-osm ולא נשברת |
HOTEL_GUEST_NATIONALITY | לאום האורח לתמחור (ברירת מחדל IL) |