@relvo/mcp 0.1.2 → 0.1.3
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.
- package/README.md +90 -5
- package/bin/relvo-mcp.mjs +1 -1
- package/dist/client/agent-run.js +681 -0
- package/dist/client/browser.js +650 -0
- package/dist/client/evm-wallet.js +214 -0
- package/dist/client/index.js +3 -360
- package/dist/client/local-input.js +18 -11
- package/dist/client/local-profile.js +22 -110
- package/dist/client/local-setup.js +0 -1
- package/dist/client/tool-name-hints.js +48 -0
- package/dist/client/wallet-access.js +207 -0
- package/dist/mcp/agent-server.js +237 -0
- package/dist/mcp/context.js +3 -2
- package/dist/mcp/host-schema.js +47 -0
- package/dist/mcp/instructions.js +12 -2
- package/dist/mcp/local-upload.js +12 -4
- package/dist/mcp/operations.js +30 -0
- package/dist/mcp/runtime.js +25 -5
- package/dist/mcp/server.js +191 -37
- package/dist/mcp/version.js +1 -0
- package/dist/schema/accountRequestMethods.js +1 -0
- package/dist/schema/cronValidator.js +150 -0
- package/dist/schema/platformApiKey.js +5 -5
- package/dist/schema/platformBillingConstants.js +3 -0
- package/dist/schema/platformCapabilityFamilies.js +217 -0
- package/dist/schema/platformConnectionCatalog.js +20 -3
- package/dist/schema/platformConnectionPolicy.js +26 -0
- package/dist/schema/platformCustomTools.js +113 -0
- package/dist/schema/platformFunding.js +9 -0
- package/dist/schema/platformHttpConstraints.js +7 -0
- package/dist/schema/platformMcpTools.js +7 -0
- package/dist/schema/platformMetaTools.js +8 -6
- package/dist/schema/platformMonitorSchedule.js +123 -0
- package/dist/schema/platformMonitors.js +159 -0
- package/dist/schema/platformOAuth.js +7 -0
- package/dist/schema/platformPublicExecution.js +53 -15
- package/dist/schema/platformRoutes.js +61 -1
- package/dist/schema/platformSkills.js +1 -1
- package/dist/schema/platformTeamSecretName.js +3 -0
- package/dist/schema/platformTeamSkills.js +49 -0
- package/dist/schema/platformToolAccess.js +13 -0
- package/dist/schema/platformTriggers.js +152 -0
- package/dist/schema/platformWorkflows.js +100 -0
- package/dist/schema/scheduleTimeZone.js +15 -0
- package/dist/schema/toolDiscovery.js +20 -3
- package/dist/schema/toolInputRepair.js +110 -0
- package/dist/schema/toolResultContext.js +1 -1
- package/package.json +1 -1
- package/dist/schema/amazonAds.js +0 -13
- package/dist/schema/oauth2.js +0 -64
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
export const AUTO_CAPABILITY_VARIANTS = {
|
|
2
|
+
generate_video: ["generate_openrouter_video"],
|
|
3
|
+
generate_avatar_video: ["animate_kling_avatar", "generate_omnihuman_video"],
|
|
4
|
+
search_people: ["apollo_people_search", "anyapi_apollo_people_search", "anyapi_people_search_ai_ark", "anyapi_people_search_crustdata",
|
|
5
|
+
"anyapi_people_search_crustdata_v3", "anyapi_people_search_fullenrich", "anyapi_people_search_lusha", "anyapi_people_search_peopledatalabs",
|
|
6
|
+
"anyapi_people_search_prospeo", "anyapi_people_search_quickenrich", "pdl_people_search", "prospeo_people_search",
|
|
7
|
+
"firecrawl_fullenrich_people_search", "brightdata_business_people_search"],
|
|
8
|
+
search_organizations: ["apollo_company_search", "anyapi_apollo_organizations_search", "anyapi_company_search_ai_ark",
|
|
9
|
+
"anyapi_company_search_crustdata", "anyapi_company_search_crustdata_v3", "anyapi_company_search_fullenrich",
|
|
10
|
+
"anyapi_company_search_peopledatalabs", "anyapi_company_search_prospeo", "anyapi_company_search_quickenrich", "pdl_company_search",
|
|
11
|
+
"prospeo_company_search", "firecrawl_fullenrich_companies_search", "brightdata_business_company_search",
|
|
12
|
+
"predictleads_discover_companies", "predictleads_discover_extended_companies"],
|
|
13
|
+
find_companies: ["hunter_discover", "hunter_discover_people"],
|
|
14
|
+
enrich_organization: ["apollo_company_enrich", "apollo_companies_enrich", "anyapi_apollo_organization_enrich",
|
|
15
|
+
"anyapi_apollo_organizations_bulk_enrich", "anyapi_company_enrichment_crustdata", "anyapi_company_enrichment_crustdata_v3",
|
|
16
|
+
"anyapi_company_enrichment_lusha", "anyapi_company_enrichment_peopledatalabs", "anyapi_company_enrichment_prospeo", "pdl_company_enrich",
|
|
17
|
+
"pdl_companies_bulk_enrich", "prospeo_company_enrich", "prospeo_companies_enrich", "hunter_company_enrich", "findymail_company_enrich",
|
|
18
|
+
"firecrawl_datalegion_companies_enrich_base", "firecrawl_datalegion_companies_enrich_premium", "firecrawl_fullenrich_companies_lookup",
|
|
19
|
+
"predictleads_company", "predictleads_extended_company"],
|
|
20
|
+
get_organization_job_postings: ["apollo_company_jobs", "anyapi_apollo_organization_jobs"],
|
|
21
|
+
find_work_email: ["hunter_email_find", "findymail_email_find", "findymail_email_find_profile", "anyapi_email_find",
|
|
22
|
+
"anyapi_email_finding_dropleads", "anyapi_email_finding_icypeas", "anyapi_email_finding_quickenrich", "anyapi_email_finding_zerobounce",
|
|
23
|
+
"anyapi_linkedin_email", "anyapi_person_enrichment_bettercontact"],
|
|
24
|
+
find_company_emails: ["hunter_domain_search", "anyapi_email_finding_hunter_domain", "findymail_domain_contacts",
|
|
25
|
+
"anyapi_people_search_quickenrich_company"],
|
|
26
|
+
verify_email: ["hunter_email_verify", "debounce_email_verify", "findymail_email_verify", "anyapi_email_verify",
|
|
27
|
+
"anyapi_email_verification_allegrow", "anyapi_email_verification_bounceban", "anyapi_email_verification_icypeas",
|
|
28
|
+
"anyapi_email_verification_zerobounce"],
|
|
29
|
+
search_x_posts: ["anyapi_twitter_search", "tikhub_x_web_fetch_search_timeline"],
|
|
30
|
+
fetch_x_posts: ["tikhub_x_web_fetch_tweet_detail"],
|
|
31
|
+
fetch_x_posts_by_url: ["anyapi_twitter_tweet", "brightdata_twitter_posts_collect_by_url"],
|
|
32
|
+
get_x_post_comments: ["anyapi_twitter_replies", "tikhub_x_web_fetch_post_comments"],
|
|
33
|
+
get_x_profile: ["anyapi_twitter_profile", "tikhub_x_web_fetch_user_profile", "brightdata_twitter_profiles_collect_by_url"],
|
|
34
|
+
get_x_trends: ["anyapi_twitter_trends", "tikhub_x_web_fetch_trending"],
|
|
35
|
+
search_x_communities: ["anyapi_twitter_search_communities", "tikhub_x_web_fetch_search_communities"],
|
|
36
|
+
get_x_community_posts: ["anyapi_twitter_community_tweets", "tikhub_x_web_fetch_community_timeline"],
|
|
37
|
+
search_threads: ["anyapi_threads_search", "apify_threads_search", "tikhub_threads_web_search_posts", "tikhub_threads_web_search_top",
|
|
38
|
+
"tikhub_threads_web_search_recent"],
|
|
39
|
+
search_threads_profiles: ["anyapi_threads_search_users", "tikhub_threads_web_search_profiles"],
|
|
40
|
+
fetch_threads_post: ["anyapi_threads_post", "tikhub_threads_web_fetch_post_detail", "tikhub_threads_web_fetch_post_detail_v2",
|
|
41
|
+
"brightdata_threads_posts_collect_by_url"],
|
|
42
|
+
get_bluesky_profile: ["anyapi_bluesky_profile"],
|
|
43
|
+
get_instagram_profile: ["anyapi_instagram_profile", "anyapi_instagram_basic_profile", "tikhub_instagram_v1_fetch_user_info_by_username",
|
|
44
|
+
"tikhub_instagram_v1_fetch_user_info_by_username_v2", "tikhub_instagram_v1_fetch_user_info_by_username_v3",
|
|
45
|
+
"tikhub_instagram_v2_fetch_user_info", "tikhub_instagram_v3_get_user_profile", "apify_instagram_profile",
|
|
46
|
+
"brightdata_instagram_profiles_collect_by_url", "brightdata_instagram_profiles_discover_by_username", "searchapi_instagram_profile", "serpapi_instagram_profile",
|
|
47
|
+
"firecrawl_oriane_instagram_profile_lookup"],
|
|
48
|
+
search_instagram_posts: ["anyapi_instagram_user_posts", "tikhub_instagram_v1_fetch_user_posts", "tikhub_instagram_v1_fetch_user_posts_v2",
|
|
49
|
+
"tikhub_instagram_v2_fetch_user_posts", "tikhub_instagram_v3_get_user_posts", "apify_instagram_posts",
|
|
50
|
+
"brightdata_instagram_posts_discover_by_url"],
|
|
51
|
+
search_instagram_reels: ["anyapi_instagram_user_reels", "tikhub_instagram_v1_fetch_user_reels", "tikhub_instagram_v2_fetch_user_reels",
|
|
52
|
+
"tikhub_instagram_v3_get_user_reels", "apify_instagram_reels", "brightdata_instagram_reels_discover_by_url",
|
|
53
|
+
"brightdata_instagram_reels_discover_by_url_all_reels"],
|
|
54
|
+
search_instagram_by_keyword: ["anyapi_instagram_hashtag_recent_posts", "anyapi_instagram_hashtag_top_posts", "anyapi_instagram_search_hashtag",
|
|
55
|
+
"tikhub_instagram_v1_fetch_hashtag_posts", "tikhub_instagram_v2_fetch_hashtag_posts", "tikhub_instagram_v3_get_hashtag_posts",
|
|
56
|
+
"apify_instagram_hashtags"],
|
|
57
|
+
search_instagram_keyword: ["anyapi_instagram_search", "tikhub_instagram_v1_fetch_search", "tikhub_instagram_v2_search_users",
|
|
58
|
+
"tikhub_instagram_v2_search_hashtags", "tikhub_instagram_v3_search_users", "tikhub_instagram_v3_search_hashtags"],
|
|
59
|
+
search_instagram_phrase: ["tikhub_instagram_v2_general_search", "tikhub_instagram_v3_general_search"],
|
|
60
|
+
search_instagram_places: ["anyapi_instagram_search_locations", "tikhub_instagram_v2_search_locations",
|
|
61
|
+
"tikhub_instagram_v2_search_by_coordinates", "tikhub_instagram_v3_search_by_coordinates"],
|
|
62
|
+
search_instagram_audio: ["anyapi_instagram_audio_reels", "tikhub_instagram_v1_fetch_music_posts", "tikhub_instagram_v2_fetch_music_posts",
|
|
63
|
+
"tikhub_instagram_v3_get_music_posts", "tikhub_instagram_v2_search_music"],
|
|
64
|
+
get_instagram_post_comments: ["anyapi_instagram_post_comments", "tikhub_instagram_v1_fetch_post_comments_v2",
|
|
65
|
+
"tikhub_instagram_v2_fetch_post_comments", "tikhub_instagram_v3_get_post_comments", "brightdata_instagram_comments_collect_by_url",
|
|
66
|
+
"searchapi_instagram_comments"],
|
|
67
|
+
get_instagram_comment_replies: ["anyapi_instagram_comment_replies", "tikhub_instagram_v1_fetch_comment_replies",
|
|
68
|
+
"tikhub_instagram_v2_fetch_comment_replies", "tikhub_instagram_v3_get_comment_replies"],
|
|
69
|
+
get_instagram_stories: ["anyapi_instagram_stories_full", "anyapi_instagram_stories_thin", "tikhub_instagram_v2_fetch_user_stories",
|
|
70
|
+
"tikhub_instagram_v3_get_user_stories"],
|
|
71
|
+
get_instagram_highlights: ["anyapi_instagram_user_highlights", "tikhub_instagram_v2_fetch_user_highlights",
|
|
72
|
+
"tikhub_instagram_v3_get_user_highlights"],
|
|
73
|
+
get_instagram_tagged_posts: ["anyapi_instagram_tagged_posts", "tikhub_instagram_v1_fetch_user_tagged_posts",
|
|
74
|
+
"tikhub_instagram_v2_fetch_user_tagged_posts", "tikhub_instagram_v3_get_user_tagged_posts"],
|
|
75
|
+
get_instagram_former_usernames: ["tikhub_instagram_v3_get_user_former_usernames"],
|
|
76
|
+
get_instagram_account_network: ["anyapi_instagram_followers", "anyapi_instagram_following", "tikhub_instagram_v2_fetch_user_followers",
|
|
77
|
+
"tikhub_instagram_v2_fetch_user_following", "tikhub_instagram_v3_get_user_followers", "tikhub_instagram_v3_get_user_following"],
|
|
78
|
+
get_instagram_reposts: ["anyapi_instagram_user_reposts", "tikhub_instagram_v1_fetch_user_reposts"],
|
|
79
|
+
find_similar_instagram_accounts: ["anyapi_instagram_similar_profiles", "tikhub_instagram_v1_fetch_related_profiles",
|
|
80
|
+
"tikhub_instagram_v2_fetch_similar_users", "tikhub_instagram_v3_get_similar_users"],
|
|
81
|
+
scrape_facebook: ["anyapi_facebook_profile", "brightdata_facebook_pages_and_profiles_collect_by_url", "searchapi_facebook_business_page",
|
|
82
|
+
"serpapi_facebook_profile"],
|
|
83
|
+
scrape_facebook_posts: ["anyapi_facebook_profile_posts", "apify_facebook_posts", "brightdata_facebook_pages_posts_collect_by_url"],
|
|
84
|
+
search_facebook: ["anyapi_facebook_search_pages", "anyapi_facebook_page_lookup", "apify_facebook_search"],
|
|
85
|
+
search_facebook_posts: ["anyapi_facebook_search_posts", "apify_facebook_post_search"],
|
|
86
|
+
search_facebook_people: ["apify_facebook_people_search"],
|
|
87
|
+
fetch_facebook_post: ["anyapi_facebook_post", "brightdata_facebook_posts_collect_by_url"],
|
|
88
|
+
search_tiktok_posts: ["anyapi_tiktok_search_keyword", "anyapi_tiktok_search_top", "anyapi_tiktok_search_hashtag", "anyapi_tiktok_hashtag_videos",
|
|
89
|
+
"anyapi_tiktok_profile_videos", "tikhub_tiktok_app_v3_fetch_video_search_result", "tikhub_tiktok_web_fetch_search_video",
|
|
90
|
+
"tikhub_tiktok_app_v3_fetch_hashtag_video_list", "tikhub_tiktok_web_fetch_tag_post", "tikhub_tiktok_app_v3_fetch_user_post_videos",
|
|
91
|
+
"tikhub_tiktok_app_v3_fetch_user_post_videos_v2", "tikhub_tiktok_app_v3_fetch_user_post_videos_v3", "tikhub_tiktok_web_fetch_user_post",
|
|
92
|
+
"searchapi_tiktok_search", "searchapi_tiktok_profile_videos", "brightdata_tiktok_posts_discover_by_keyword",
|
|
93
|
+
"brightdata_tiktok_posts_discover_by_profile_url", "brightdata_tiktok_posts_by_profile_fast_api_collect_by_url",
|
|
94
|
+
"firecrawl_oriane_tiktok_posts_search", "apify_tiktok_scrape"],
|
|
95
|
+
get_tiktok_profile: ["anyapi_tiktok_profile", "tikhub_tiktok_web_fetch_user_profile", "tikhub_tiktok_app_v3_handler_user_profile",
|
|
96
|
+
"searchapi_tiktok_profile", "brightdata_tiktok_profiles_collect_by_url", "firecrawl_oriane_tiktok_profile_lookup"],
|
|
97
|
+
get_tiktok_comments: ["anyapi_tiktok_video_comments", "tiktok_comments", "tikhub_tiktok_web_fetch_post_comment",
|
|
98
|
+
"tikhub_tiktok_app_v3_fetch_video_comments", "searchapi_tiktok_comments", "brightdata_tiktok_comments_collect_by_url"],
|
|
99
|
+
get_tiktok_comment_replies: ["anyapi_tiktok_comment_replies", "tikhub_tiktok_web_fetch_post_comment_reply",
|
|
100
|
+
"tikhub_tiktok_app_v3_fetch_video_comment_replies"],
|
|
101
|
+
get_tiktok_video_metrics: ["tikhub_tiktok_analytics_fetch_video_metrics", "anyapi_tiktok_video", "tikhub_tiktok_app_v3_fetch_one_video",
|
|
102
|
+
"tikhub_tiktok_app_v3_fetch_one_video_v2", "tikhub_tiktok_app_v3_fetch_one_video_v3", "tikhub_tiktok_web_fetch_post_detail",
|
|
103
|
+
"tikhub_tiktok_web_fetch_post_detail_v2", "searchapi_tiktok_video", "brightdata_tiktok_posts_collect_by_url",
|
|
104
|
+
"firecrawl_oriane_tiktok_video_lookup"],
|
|
105
|
+
get_tiktok_video_transcript: ["anyapi_tiktok_video_transcript", "anyapi_tiktok_video_transcript_full", "searchapi_tiktok_transcripts",
|
|
106
|
+
"apify_tiktok_transcript"],
|
|
107
|
+
get_tiktok_sound_posts: ["anyapi_tiktok_song_videos", "tikhub_tiktok_app_v3_fetch_music_video_list"],
|
|
108
|
+
get_tiktok_sounds: ["tikhub_tiktok_app_v3_fetch_music_search_result", "tikhub_tiktok_app_v3_fetch_music_chart_list"],
|
|
109
|
+
get_tiktok_trends: ["apify_tiktok_trends", "anyapi_tiktok_trending_feed"],
|
|
110
|
+
get_tiktok_trending_hashtags: ["anyapi_tiktok_trending_hashtags", "tikhub_tiktok_ads_get_trends_hashtag_list"],
|
|
111
|
+
get_tiktok_hashtag_trend_detail: ["tikhub_tiktok_ads_get_trends_hashtag_detail"],
|
|
112
|
+
get_tiktok_search_insights: ["tikhub_tiktok_app_v3_fetch_creator_search_insights"],
|
|
113
|
+
get_tiktok_search_insight_detail: ["tikhub_tiktok_app_v3_fetch_creator_search_insights_detail"],
|
|
114
|
+
get_tiktok_comment_themes: ["tikhub_tiktok_analytics_fetch_comment_keywords"],
|
|
115
|
+
get_tiktok_video_article: ["tikhub_tiktok_web_fetch_post_ai_article"],
|
|
116
|
+
check_tiktok_video_authenticity: ["tikhub_tiktok_analytics_detect_fake_views"],
|
|
117
|
+
find_similar_tiktok_accounts: ["tikhub_tiktok_app_v3_fetch_similar_user_recommendations"],
|
|
118
|
+
search_tiktok_creators: ["anyapi_tiktok_search_users", "tikhub_tiktok_app_v3_fetch_user_search_result"],
|
|
119
|
+
get_tiktok_account_region: ["anyapi_tiktok_profile_region"],
|
|
120
|
+
get_tiktok_ad_detail: ["tikhub_tiktok_ads_get_ads_detail"],
|
|
121
|
+
search_tiktok_top_ads: ["anyapi_tiktok_top_ads_search", "tikhub_tiktok_ads_search_ads"],
|
|
122
|
+
search_tiktok_shop_products: ["anyapi_tiktok_shop_search", "tikhub_tiktok_shop_web_fetch_search_products_list",
|
|
123
|
+
"tikhub_tiktok_shop_web_fetch_search_products_list_v2", "brightdata_tiktok_shop_discover_by_keyword"],
|
|
124
|
+
get_tiktok_shop_product: ["anyapi_tiktok_shop_product", "tikhub_tiktok_shop_web_fetch_product_detail",
|
|
125
|
+
"tikhub_tiktok_shop_web_fetch_product_detail_v3"],
|
|
126
|
+
get_tiktok_shop_product_reviews: ["anyapi_tiktok_shop_product_reviews", "tikhub_tiktok_shop_web_fetch_product_reviews_v2"],
|
|
127
|
+
search_youtube: ["dataforseo_serp_youtube_organic_advanced", "anyapi_youtube_search", "tikhub_youtube_web_search_video", "tikhub_youtube_web_v2_get_general_search_v2",
|
|
128
|
+
"brightdata_youtube_videos_discover_by_keyword", "searchapi_youtube", "serpapi_youtube"],
|
|
129
|
+
search_youtube_channels: ["tikhub_youtube_web_search_channel", "tikhub_youtube_web_v2_search_channels",
|
|
130
|
+
"brightdata_youtube_channels_discover_by_keyword"],
|
|
131
|
+
search_youtube_shorts: ["anyapi_youtube_search_shorts", "tikhub_youtube_web_v2_get_shorts_search", "tikhub_youtube_web_v2_get_shorts_search_v2"],
|
|
132
|
+
get_youtube_video_info: ["dataforseo_serp_youtube_video_info_advanced", "anyapi_youtube_video", "tikhub_youtube_web_v2_get_video_info", "tikhub_youtube_web_v2_get_video_info_v2",
|
|
133
|
+
"brightdata_youtube_videos_collect_by_url", "searchapi_youtube_video", "serpapi_youtube_video"],
|
|
134
|
+
get_youtube_comments: ["dataforseo_serp_youtube_video_comments_advanced", "anyapi_youtube_video_comments", "tikhub_youtube_web_v2_get_video_comments", "searchapi_youtube_comments",
|
|
135
|
+
"brightdata_youtube_comments_collect_by_url"],
|
|
136
|
+
get_youtube_comment_replies: ["anyapi_youtube_comment_replies", "tikhub_youtube_web_v2_get_video_comment_replies",
|
|
137
|
+
"tikhub_youtube_web_v2_get_post_comment_replies"],
|
|
138
|
+
get_youtube_channel_about: ["tikhub_youtube_web_v2_get_channel_description", "tikhub_youtube_web_get_channel_info", "anyapi_youtube_channel",
|
|
139
|
+
"searchapi_youtube_channel", "serpapi_youtube_channel"],
|
|
140
|
+
get_youtube_channel_shorts: ["anyapi_youtube_channel_shorts", "tikhub_youtube_web_v2_get_channel_shorts",
|
|
141
|
+
"tikhub_youtube_web_get_channel_short_videos", "searchapi_youtube_channel_shorts"],
|
|
142
|
+
get_youtube_community_posts: ["anyapi_youtube_channel_community_posts", "tikhub_youtube_web_v2_get_channel_community_posts",
|
|
143
|
+
"searchapi_youtube_channel_posts"],
|
|
144
|
+
get_youtube_related_videos: ["tikhub_youtube_web_get_relate_video", "tikhub_youtube_web_v2_get_related_videos"],
|
|
145
|
+
get_youtube_transcript: ["dataforseo_serp_youtube_video_subtitles_advanced", "anyapi_youtube_video_transcript", "anyapi_youtube_video_transcript_full", "searchapi_youtube_transcripts",
|
|
146
|
+
"serpapi_youtube_video_transcript"],
|
|
147
|
+
get_youtube_trends: ["searchapi_youtube_trends"],
|
|
148
|
+
search_reddit: ["anyapi_reddit_search", "tikhub_reddit_app_fetch_dynamic_search", "brightdata_reddit_posts_discover_by_keyword",
|
|
149
|
+
"apify_reddit_scrape"],
|
|
150
|
+
fetch_reddit_post: ["anyapi_reddit_post", "tikhub_reddit_app_fetch_post_details", "brightdata_reddit_posts_collect_by_url"],
|
|
151
|
+
get_reddit_post_comments: ["anyapi_reddit_post_comments", "tikhub_reddit_app_fetch_post_comments", "brightdata_reddit_comments_collect_by_url"],
|
|
152
|
+
get_subreddit_posts: ["anyapi_reddit_subreddit_posts", "tikhub_reddit_app_fetch_subreddit_feed",
|
|
153
|
+
"brightdata_reddit_posts_discover_by_subreddit_url"],
|
|
154
|
+
get_subreddit_info: ["anyapi_reddit_subreddit_details", "tikhub_reddit_app_fetch_subreddit_info"],
|
|
155
|
+
get_reddit_popular_posts: ["tikhub_reddit_app_fetch_popular_feed", "anyapi_reddit_trending_posts"],
|
|
156
|
+
get_reddit_trending_searches: ["tikhub_reddit_app_fetch_trending_searches"],
|
|
157
|
+
get_reddit_user_profile: ["anyapi_reddit_profile", "tikhub_reddit_app_fetch_user_profile", "brightdata_reddit_profiles_1c7f260527ac"],
|
|
158
|
+
get_reddit_user_posts: ["anyapi_reddit_user_posts", "tikhub_reddit_app_fetch_user_posts"],
|
|
159
|
+
get_reddit_user_comments: ["anyapi_reddit_user_comments", "tikhub_reddit_app_fetch_user_comments"],
|
|
160
|
+
fetch_linkedin_post: ["anyapi_linkedin_post", "tikhub_linkedin_web_v2_get_post_detail", "brightdata_linkedin_posts_collect_by_url"],
|
|
161
|
+
get_linkedin_post_comments: ["anyapi_linkedin_post_comments", "tikhub_linkedin_web_v2_get_post_comments"],
|
|
162
|
+
get_linkedin_job_posting: ["anyapi_linkedin_job", "tikhub_linkedin_web_v2_get_job_detail", "searchapi_linkedin_job_details",
|
|
163
|
+
"brightdata_linkedin_jobs_collect_by_url"],
|
|
164
|
+
search_linkedin_job_postings: ["anyapi_linkedin_jobs", "anyapi_linkedin_jobs_thin", "tikhub_linkedin_web_v2_search_jobs",
|
|
165
|
+
"searchapi_linkedin_jobs", "brightdata_linkedin_jobs_discover_by_keyword"],
|
|
166
|
+
get_substack_post: ["firecrawl_substack_com_newsletters_post"],
|
|
167
|
+
list_substack_posts: ["firecrawl_substack_com_newsletters_archive", "anyapi_substack_posts"],
|
|
168
|
+
search_podcasts: ["firecrawl_particle_podcasts_search"],
|
|
169
|
+
search_podcast_episodes: ["firecrawl_particle_podcasts_episodes_search"],
|
|
170
|
+
get_podcast_show: ["firecrawl_particle_podcasts_show", "anyapi_spotify_podcast"],
|
|
171
|
+
get_podcast_episodes: ["firecrawl_particle_podcasts_show_episodes", "anyapi_spotify_podcast_episodes"],
|
|
172
|
+
web_search: ["search_web", "exa_search", "brave_web", "anyapi_brave_search", "crawl4ai_search"],
|
|
173
|
+
search_news: ["dataforseo_serp_google_news_advanced", "dataforseo_serp_google_news_html", "anyapi_google_news", "searchapi_google_news", "searchapi_google_news_light", "searchapi_bing_news", "brave_news",
|
|
174
|
+
"anyapi_hackernews_search", "serpapi_google_news", "serpapi_google_news_light", "serpapi_bing_news", "serpapi_duckduckgo_news", "serpapi_baidu_news"],
|
|
175
|
+
search_image: ["dataforseo_serp_google_images_advanced", "dataforseo_serp_google_images_html", "brave_images", "anyapi_google_images", "searchapi_google_images", "searchapi_google_images_light", "searchapi_bing_images",
|
|
176
|
+
"searchapi_duckduckgo_images", "serpapi_google_images", "serpapi_google_images_light", "serpapi_bing_images", "serpapi_yahoo_images", "serpapi_yandex_images"],
|
|
177
|
+
search_maps: ["dataforseo_serp_google_maps_advanced", "dataforseo_serp_google_local_finder_advanced", "dataforseo_serp_google_local_finder_html",
|
|
178
|
+
"dataforseo_business_business_listings_search", "anyapi_maps_search", "searchapi_google_maps", "firecrawl_firecrawl_maps_businesses_search", "serpapi_google_maps", "serpapi_google_local",
|
|
179
|
+
"serpapi_bing_maps", "serpapi_apple_maps", "serpapi_duckduckgo_maps", "serpapi_naver_maps"],
|
|
180
|
+
search_github: ["searchapi_github_search"],
|
|
181
|
+
serp_lookup: ["dataforseo_serp_google_organic_advanced", "dataforseo_serp_google_organic_regular", "dataforseo_serp_google_organic_html",
|
|
182
|
+
"dataforseo_serp_bing_organic_advanced", "dataforseo_serp_bing_organic_regular", "dataforseo_serp_bing_organic_html",
|
|
183
|
+
"dataforseo_serp_yahoo_organic_advanced", "dataforseo_serp_yahoo_organic_regular", "dataforseo_serp_yahoo_organic_html",
|
|
184
|
+
"dataforseo_serp_baidu_organic_advanced", "dataforseo_serp_baidu_organic_regular", "dataforseo_serp_baidu_organic_html",
|
|
185
|
+
"dataforseo_serp_naver_organic_advanced", "dataforseo_serp_naver_organic_regular", "dataforseo_serp_naver_organic_html",
|
|
186
|
+
"dataforseo_serp_seznam_organic_advanced", "dataforseo_serp_seznam_organic_regular", "dataforseo_serp_seznam_organic_html", "anyapi_google_search", "anyapi_google_search_100", "anyapi_bing_search", "searchapi_google", "searchapi_google_light",
|
|
187
|
+
"searchapi_google_rank_tracking", "searchapi_bing", "searchapi_naver", "serpapi_google", "serpapi_google_light", "serpapi_bing", "serpapi_naver", "crawl4ai_serp"],
|
|
188
|
+
google_ai_mode: ["dataforseo_serp_google_ai_mode_advanced", "dataforseo_serp_google_ai_mode_html", "anyapi_google_ai_mode", "searchapi_google_ai_mode", "serpapi_google_ai_mode"],
|
|
189
|
+
google_ai_overview: ["anyapi_google_ai_overview", "searchapi_google_ai_overview", "serpapi_google_ai_overview"],
|
|
190
|
+
google_trends: ["searchapi_google_trends_trending_now", "firecrawl_firecrawl_trends_trends_trending_now", "serpapi_google_trends_trending_now"],
|
|
191
|
+
google_trends_interest: ["dataforseo_keywords_google_trends_explore", "dataforseo_keywords_dataforseo_trends_explore", "anyapi_google_trends", "searchapi_google_trends", "firecrawl_firecrawl_trends_trends_interest_over_time", "serpapi_google_trends"],
|
|
192
|
+
seo_keyword_metrics: ["dataforseo_labs_google_keyword_overview", "dataforseo_keywords_google_ads_search_volume", "dataforseo_labs_google_bulk_keyword_difficulty"],
|
|
193
|
+
seo_domain_overview: ["dataforseo_labs_google_domain_rank_overview", "dataforseo_labs_google_bulk_traffic_estimation"],
|
|
194
|
+
seo_domain_keywords: ["dataforseo_labs_google_ranked_keywords", "dataforseo_labs_google_keywords_for_site"],
|
|
195
|
+
get_website_traffic: ["anyapi_similarweb_overview", "firecrawl_similarweb_web_traffic"],
|
|
196
|
+
search_meta_ads_library: ["anyapi_facebook_ads_search", "anyapi_facebook_company_ads", "searchapi_meta_ad_library",
|
|
197
|
+
"brightdata_meta_ad_library__fe525218deb2", "apify_facebook_ads"],
|
|
198
|
+
search_meta_ad_library_details: ["anyapi_facebook_ad_details", "anyapi_facebook_ad_details_full", "searchapi_meta_ad_library_ad_details"],
|
|
199
|
+
search_meta_ad_library_pages: ["anyapi_facebook_search_companies", "searchapi_meta_ad_library_page_search"],
|
|
200
|
+
search_google_ads_transparency: ["anyapi_google_ads_company_ads", "anyapi_google_ads_search", "searchapi_google_ads_transparency_center",
|
|
201
|
+
"serpapi_google_ads_transparency_center"],
|
|
202
|
+
search_google_ads_transparency_details: ["anyapi_google_ads_ad_details", "searchapi_google_ads_transparency_center_ad_details",
|
|
203
|
+
"serpapi_google_ads_transparency_center_ad_details"],
|
|
204
|
+
search_linkedin_ads_library: ["anyapi_linkedin_ads_search", "anyapi_linkedin_ad", "searchapi_linkedin_ad_library",
|
|
205
|
+
"searchapi_linkedin_ad_library_ad_details"],
|
|
206
|
+
search_tiktok_ads_library: ["anyapi_tiktok_ad_library_search", "searchapi_tiktok_ads_library"],
|
|
207
|
+
search_tiktok_ads_library_details: ["anyapi_tiktok_ad_library_ad", "searchapi_tiktok_ads_library_ad_details"],
|
|
208
|
+
search_mobile_apps: ["dataforseo_app_data_apple_app_searches_advanced", "dataforseo_app_data_google_app_searches_advanced", "dataforseo_app_data_google_app_searches_html",
|
|
209
|
+
"dataforseo_app_data_apple_app_listings_search", "dataforseo_app_data_google_app_listings_search", "anyapi_appstore_search", "anyapi_playstore_search", "searchapi_apple_app_store", "searchapi_google_play_store",
|
|
210
|
+
"firecrawl_apps_apple_com_apps_search", "firecrawl_play_google_com_media_app_search", "serpapi_apple_app_store", "serpapi_google_play"],
|
|
211
|
+
get_mobile_app: ["dataforseo_app_data_apple_app_info_advanced", "dataforseo_app_data_google_app_info_advanced", "dataforseo_app_data_google_app_info_html", "anyapi_appstore_app", "anyapi_playstore_app", "searchapi_google_play_product", "firecrawl_apps_apple_com_apps_app",
|
|
212
|
+
"firecrawl_play_google_com_media_app", "serpapi_google_play_product", "serpapi_apple_product"],
|
|
213
|
+
get_public_reviews: ["dataforseo_business_google_reviews", "dataforseo_business_google_extended_reviews", "dataforseo_business_trustpilot_reviews",
|
|
214
|
+
"dataforseo_business_tripadvisor_reviews", "anyapi_trustpilot_reviews", "anyapi_g2_reviews", "anyapi_maps_reviews", "brightdata_trustpilot_reviews_collect_by_url",
|
|
215
|
+
"brightdata_g2_reviews_collect_by_url", "brightdata_google_maps_reviews_collect_by_url", "searchapi_google_maps_reviews", "serpapi_google_maps_reviews",
|
|
216
|
+
"firecrawl_firecrawl_maps_businesses_reviews"],
|
|
217
|
+
};
|
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { TOOL_RESULT_CONTEXT_MAX_BYTES } from "./toolResultContext.js";
|
|
3
|
+
import { platformConnectionPolicySchema } from "./platformConnectionPolicy.js";
|
|
4
|
+
import { platformAppApiPathSchema } from "./platformHttpConstraints.js";
|
|
5
|
+
export { platformOAuthScopeAlternativesSchema, platformOAuthPermissionSchema } from "./platformOAuth.js";
|
|
6
|
+
export const PLATFORM_APP_ID_PATTERN = /^[a-z0-9][a-z0-9_]{0,63}$/;
|
|
7
|
+
export const platformAppIdSchema = z.string().regex(PLATFORM_APP_ID_PATTERN);
|
|
8
|
+
export const platformAppConnectionProviderSchema = z.templateLiteral(["app:", platformAppIdSchema]);
|
|
3
9
|
export const platformConnectionProviderSchema = z.enum(["x", "gsc", "ga", "google", "github", "notion", "slack", "linear", "linkedin", "pinterest", "tiktok", "microsoftAds", "amazonAds"]);
|
|
10
|
+
export const platformAnyConnectionProviderSchema = z.union([platformConnectionProviderSchema, platformAppConnectionProviderSchema]);
|
|
4
11
|
export function platformConnectionOAuthProvider(provider) {
|
|
5
12
|
return provider === "gsc" || provider === "ga" ? "google" : provider === "microsoftAds" ? "microsoft" : provider === "amazonAds" ? "amazon" : provider;
|
|
6
13
|
}
|
|
7
|
-
export const PLATFORM_CONNECTION_CATALOG_LIMITS = { maxRows: 20, responseBytes: 65_536, metadataBytes: 60_000, contextBytes: TOOL_RESULT_CONTEXT_MAX_BYTES };
|
|
14
|
+
export const PLATFORM_CONNECTION_CATALOG_LIMITS = { maxRows: 20, responseBytes: 65_536, metadataBytes: 60_000, policyBytes: 8192, contextBytes: TOOL_RESULT_CONTEXT_MAX_BYTES };
|
|
8
15
|
export const PLATFORM_CONNECTION_PAGE_MAX = PLATFORM_CONNECTION_CATALOG_LIMITS.maxRows;
|
|
9
16
|
export const platformConnectionCatalogQuerySchema = z.object({
|
|
10
|
-
provider:
|
|
17
|
+
provider: platformAnyConnectionProviderSchema.optional(),
|
|
11
18
|
offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).default(0),
|
|
12
19
|
limit: z.number().int().min(1).max(PLATFORM_CONNECTION_PAGE_MAX).default(10),
|
|
13
20
|
}).strict();
|
|
21
|
+
export const platformConnectionAppSchema = z.object({ id: platformAppIdSchema, name: z.string(), docs: z.string(), baseUrl: z.string(),
|
|
22
|
+
baseUrls: z.array(z.string()).optional(),
|
|
23
|
+
apiPaths: z.array(platformAppApiPathSchema).optional(),
|
|
24
|
+
uploadUrls: z.array(z.object({ prefix: z.string(), credentials: z.boolean() })).optional(),
|
|
25
|
+
transport: z.literal("mcp").optional() });
|
|
14
26
|
export const platformConnectionCatalogSchema = z.object({
|
|
15
27
|
connections: z.array(z.object({
|
|
16
|
-
id: z.string(), provider:
|
|
28
|
+
id: z.string(), provider: platformAnyConnectionProviderSchema, label: z.string(),
|
|
17
29
|
status: z.enum(["connected", "reauth_required"]), scopes: z.array(z.string()),
|
|
30
|
+
scopeSyncedAt: z.iso.datetime().nullable().default(null),
|
|
31
|
+
personal: z.boolean().optional(),
|
|
32
|
+
policy: platformConnectionPolicySchema.nullable().optional(),
|
|
33
|
+
policyRestricted: z.boolean().optional(),
|
|
34
|
+
app: platformConnectionAppSchema.optional(),
|
|
18
35
|
})).max(PLATFORM_CONNECTION_PAGE_MAX),
|
|
19
36
|
total: z.number().int().nonnegative(), nextOffset: z.number().int().nonnegative().nullable(),
|
|
20
37
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ACCOUNT_REQUEST_HTTP_METHODS } from "./accountRequestMethods.js";
|
|
3
|
+
export const PLATFORM_CONNECTION_POLICY_LIMITS = { rules: 64, subjects: 100, conditions: 16, pathChars: 2048, pointerChars: 256, valueChars: 2048, jsonChars: 12_000,
|
|
4
|
+
operations: 2_000, operationChars: 128, signInOperationsChars: 32_000 };
|
|
5
|
+
const limits = PLATFORM_CONNECTION_POLICY_LIMITS;
|
|
6
|
+
const ids = z.array(z.string().min(1).max(128)).min(1).max(limits.subjects);
|
|
7
|
+
const condition = z.object({ pointer: z.string().startsWith("/").max(limits.pointerChars),
|
|
8
|
+
operator: z.enum(["equals", "prefix"]).default("equals"), value: z.union([z.string().max(limits.valueChars), z.number().finite(), z.boolean()]) }).strict()
|
|
9
|
+
.refine(value => value.operator !== "prefix" || typeof value.value === "string", "A prefix is a string");
|
|
10
|
+
const common = { effect: z.enum(["allow", "deny"]).default("allow"), memberIds: ids.optional(), apiKeyIds: ids.optional(),
|
|
11
|
+
directoryGroupIds: ids.optional(),
|
|
12
|
+
conditions: z.array(condition).max(limits.conditions).default([]), approvalRequired: z.boolean().default(false) };
|
|
13
|
+
const path = z.string().startsWith("/").max(limits.pathChars).refine(value => !/[?#\\\u0000-\u001f\u007f]/.test(value) && !value.includes("//") && !value.split("/").some(part => part === "." || part === ".."));
|
|
14
|
+
export const platformConnectionPolicyRuleSchema = z.discriminatedUnion("kind", [
|
|
15
|
+
z.object({ ...common, kind: z.literal("http"), methods: z.array(z.enum(ACCOUNT_REQUEST_HTTP_METHODS)).min(1).max(ACCOUNT_REQUEST_HTTP_METHODS.length),
|
|
16
|
+
path, prefix: z.boolean().default(false), baseUrl: z.string().max(limits.pathChars).optional() }).strict(),
|
|
17
|
+
z.object({ ...common, kind: z.literal("mcp"), tool: z.string().min(1).max(256) }).strict(),
|
|
18
|
+
z.object({ ...common, kind: z.literal("database"), operations: z.array(z.enum(["schema", "query", "execute"])).min(1).max(3) }).strict(),
|
|
19
|
+
z.object({ ...common, kind: z.literal("operation"), action: z.string().min(1).max(128) }).strict(),
|
|
20
|
+
]).refine(rule => rule.effect !== "deny" || !rule.approvalRequired, "A refusal cannot be approved");
|
|
21
|
+
export const PLATFORM_CONNECTION_READ_OPERATIONS = "reads";
|
|
22
|
+
const operationNames = z.array(z.string().min(1).max(limits.operationChars)).max(limits.operations);
|
|
23
|
+
export const platformConnectionOperationsSchema = z.object({ allow: operationNames.optional(), deny: operationNames.default([]) }).strict();
|
|
24
|
+
export const platformConnectionPolicySchema = z.object({ version: z.literal(1), defaultEffect: z.enum(["allow", "deny"]).default("deny"),
|
|
25
|
+
rules: z.array(platformConnectionPolicyRuleSchema).max(limits.rules), operations: platformConnectionOperationsSchema.optional() }).strict()
|
|
26
|
+
.refine(value => JSON.stringify({ ...value, operations: undefined }).length <= limits.jsonChars, "Policy is too large");
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { projectScopeSchema, platformJsonObjectSchema, PLATFORM_SANDBOX_LIMITS } from "./platformPublicExecution.js";
|
|
3
|
+
import { PLATFORM_APP_HOST_PATTERN, PLATFORM_APP_RESERVED_HEADERS } from "./platformHttpConstraints.js";
|
|
4
|
+
import { platformTeamSecretNameSchema } from "./platformTeamSecretName.js";
|
|
5
|
+
export const PLATFORM_CUSTOM_TOOL_PREFIX = "custom__";
|
|
6
|
+
export const PLATFORM_CUSTOM_TOOL_LIMITS = {
|
|
7
|
+
perProject: 100,
|
|
8
|
+
versions: 50,
|
|
9
|
+
titleChars: 80,
|
|
10
|
+
descriptionChars: 1_000,
|
|
11
|
+
codeChars: 40_000,
|
|
12
|
+
schemaBytes: 16_384,
|
|
13
|
+
inputBytes: 16_384,
|
|
14
|
+
};
|
|
15
|
+
export const PLATFORM_CUSTOM_ENDPOINT_LIMITS = { bindings: 5, baseUrlChars: 2_048, nameChars: 64, templateChars: 256, secretChars: 4_096 };
|
|
16
|
+
export const platformCustomToolKindSchema = z.enum(["python", "http"]);
|
|
17
|
+
export const platformCustomEndpointLocationSchema = z.enum(["header", "query"]);
|
|
18
|
+
const HEADER_NAME = /^[A-Za-z0-9_-]{1,64}$/;
|
|
19
|
+
const QUERY_NAME = /^[A-Za-z0-9_.$-]{1,64}$/;
|
|
20
|
+
const FORBIDDEN_BINDING_HEADER = /^(?:keep-alive|trailer|proxy-.*|x-forwarded-.*|x-real-ip|set-cookie|accept-encoding|content-type|accept)$/i;
|
|
21
|
+
const CONTROL = /[\u0000-\u001f\u007f]/;
|
|
22
|
+
export const PLATFORM_CUSTOM_ENDPOINT_SECRET_PLACEHOLDER = "{secret}";
|
|
23
|
+
export function isPlatformCustomEndpointBaseUrl(value) {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(value);
|
|
26
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.hash && !url.search && !value.includes("?") && !value.includes("#")
|
|
27
|
+
&& PLATFORM_APP_HOST_PATTERN.test(url.host) && !/(?:^|\/)\.{1,2}(?:\/|$)/.test(url.pathname) && !/[\\\s]/.test(value) && !CONTROL.test(value);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export const platformCustomEndpointBaseUrlSchema = z.string().trim().min(1).max(PLATFORM_CUSTOM_ENDPOINT_LIMITS.baseUrlChars)
|
|
34
|
+
.refine(isPlatformCustomEndpointBaseUrl, "An https URL on a public host name, without credentials, a query or a fragment")
|
|
35
|
+
.describe("The API root, e.g. https://api.example.com or https://api.example.com/v1. Requests go to a path under it.");
|
|
36
|
+
const bindingTemplateSchema = z.string().min(1).max(PLATFORM_CUSTOM_ENDPOINT_LIMITS.templateChars)
|
|
37
|
+
.refine(value => value.includes(PLATFORM_CUSTOM_ENDPOINT_SECRET_PLACEHOLDER) && value.replaceAll(PLATFORM_CUSTOM_ENDPOINT_SECRET_PLACEHOLDER, "").search(/[{}]/) === -1 && !CONTROL.test(value), "The template contains {secret} and no other braces or control characters");
|
|
38
|
+
const bindingNameCheck = (binding) => binding.location === "header"
|
|
39
|
+
? HEADER_NAME.test(binding.name) && !PLATFORM_APP_RESERVED_HEADERS.has(binding.name.toLowerCase()) && !FORBIDDEN_BINDING_HEADER.test(binding.name)
|
|
40
|
+
: QUERY_NAME.test(binding.name);
|
|
41
|
+
const bindingLocation = platformCustomEndpointLocationSchema.describe("header or query: where Relvo puts the credential on every request.");
|
|
42
|
+
const bindingName = z.string().min(1).max(PLATFORM_CUSTOM_ENDPOINT_LIMITS.nameChars).describe("The header or query parameter name, e.g. Authorization or api_key.");
|
|
43
|
+
export const platformCustomEndpointBindingInputSchema = z.object({
|
|
44
|
+
id: z.string().regex(/^[a-z0-9]{8,32}$/).optional().describe("Only when editing: the id of a saved binding whose secret is kept when no new secret is sent."),
|
|
45
|
+
location: bindingLocation,
|
|
46
|
+
name: bindingName,
|
|
47
|
+
template: bindingTemplateSchema.default(PLATFORM_CUSTOM_ENDPOINT_SECRET_PLACEHOLDER).describe("The value sent, with {secret} where the secret goes, e.g. Bearer {secret}."),
|
|
48
|
+
secret: z.string().min(1).max(PLATFORM_CUSTOM_ENDPOINT_LIMITS.secretChars).refine(value => !CONTROL.test(value), "The secret has no control characters").optional()
|
|
49
|
+
.describe("The credential. Required for a new binding without secretRef; sent again only to replace it. Relvo never returns it."),
|
|
50
|
+
secretRef: platformTeamSecretNameSchema.optional().describe("The name of a saved team secret to use instead of an inline secret. Only the console may set it."),
|
|
51
|
+
}).strict().refine(bindingNameCheck, "A valid header or query parameter name that is not reserved")
|
|
52
|
+
.refine(binding => binding.id !== undefined || binding.secret !== undefined || binding.secretRef !== undefined, "A new binding needs its secret")
|
|
53
|
+
.refine(binding => binding.secret === undefined || binding.secretRef === undefined, "A binding takes a secret or a secretRef, not both");
|
|
54
|
+
export const platformCustomEndpointBindingSchema = z.object({ id: z.string(), location: bindingLocation, name: bindingName, template: bindingTemplateSchema,
|
|
55
|
+
maskedSecret: z.string().nullable(), secretRef: platformTeamSecretNameSchema.nullable().default(null) }).strict();
|
|
56
|
+
export const platformCustomEndpointSchema = z.object({ baseUrl: platformCustomEndpointBaseUrlSchema,
|
|
57
|
+
bindings: z.array(platformCustomEndpointBindingSchema).min(1).max(PLATFORM_CUSTOM_ENDPOINT_LIMITS.bindings) }).strict();
|
|
58
|
+
export const platformCustomToolSlugSchema = z.string().regex(/^[a-z][a-z0-9_]{1,39}$/)
|
|
59
|
+
.describe("Lowercase letters, digits and underscores, starting with a letter; the tool is named custom__<slug>.");
|
|
60
|
+
export const PLATFORM_CUSTOM_TOOL_NAME = /^custom__[a-z][a-z0-9_]{1,39}$/;
|
|
61
|
+
export const platformCustomToolNameSchema = z.string().regex(PLATFORM_CUSTOM_TOOL_NAME);
|
|
62
|
+
export const isPlatformCustomToolName = (name) => PLATFORM_CUSTOM_TOOL_NAME.test(name);
|
|
63
|
+
export const platformCustomToolInputSchemaSchema = platformJsonObjectSchema
|
|
64
|
+
.refine(schema => schema.type === "object" && schema.properties !== null && typeof schema.properties === "object" && !Array.isArray(schema.properties), "The input schema is an object schema with properties")
|
|
65
|
+
.refine(schema => new TextEncoder().encode(JSON.stringify(schema)).byteLength <= PLATFORM_CUSTOM_TOOL_LIMITS.schemaBytes, "The input schema is too large");
|
|
66
|
+
export const platformCustomToolDefinitionSchema = z.object({
|
|
67
|
+
kind: z.literal("python").default("python"),
|
|
68
|
+
title: z.string().trim().min(1).max(PLATFORM_CUSTOM_TOOL_LIMITS.titleChars),
|
|
69
|
+
description: z.string().trim().min(1).max(PLATFORM_CUSTOM_TOOL_LIMITS.descriptionChars).describe("What the tool does and returns; agents choose tools by it."),
|
|
70
|
+
inputSchema: platformCustomToolInputSchemaSchema.describe("JSON Schema of the input object, e.g. {\"type\":\"object\",\"properties\":{\"days\":{\"type\":\"integer\"}},\"required\":[\"days\"]}."),
|
|
71
|
+
code: z.string().min(1).max(PLATFORM_CUSTOM_TOOL_LIMITS.codeChars)
|
|
72
|
+
.describe("Python. The validated input is the dict `input`; print the result (JSON is easiest to read back). Files of sourceExecutionIds are listed in /home/user/tool-results/manifest.json. No credentials are present."),
|
|
73
|
+
timeoutMs: z.number().int().min(1000).max(PLATFORM_SANDBOX_LIMITS.timeoutMs).default(30_000),
|
|
74
|
+
enabled: z.boolean().default(true),
|
|
75
|
+
}).strict();
|
|
76
|
+
const uniqueBindings = (bindings) => new Set(bindings.map(binding => `${binding.location}:${binding.name.toLowerCase()}`)).size === bindings.length;
|
|
77
|
+
export const platformCustomEndpointDefinitionSchema = z.object({
|
|
78
|
+
kind: z.literal("http"),
|
|
79
|
+
title: z.string().trim().min(1).max(PLATFORM_CUSTOM_TOOL_LIMITS.titleChars),
|
|
80
|
+
description: z.string().trim().min(1).max(PLATFORM_CUSTOM_TOOL_LIMITS.descriptionChars).describe("What the API is and when to use it; agents choose tools by it."),
|
|
81
|
+
baseUrl: platformCustomEndpointBaseUrlSchema,
|
|
82
|
+
bindings: z.array(platformCustomEndpointBindingInputSchema).min(1).max(PLATFORM_CUSTOM_ENDPOINT_LIMITS.bindings).refine(uniqueBindings, "Each header or query parameter is bound once")
|
|
83
|
+
.describe("Credentials Relvo adds to every request, in the header or query parameter named. Agents never see them."),
|
|
84
|
+
enabled: z.boolean().default(true),
|
|
85
|
+
}).strict();
|
|
86
|
+
const slugged = { slug: platformCustomToolSlugSchema };
|
|
87
|
+
const versioned = { expectedVersion: z.number().int().min(1) };
|
|
88
|
+
export const platformCustomToolCreateSchema = z.union([platformCustomToolDefinitionSchema.extend(slugged).strict(), platformCustomEndpointDefinitionSchema.extend(slugged).strict()]);
|
|
89
|
+
export const platformCustomToolUpdateSchema = z.union([platformCustomToolDefinitionSchema.extend(versioned).strict(), platformCustomEndpointDefinitionSchema.extend(versioned).strict()]);
|
|
90
|
+
export const platformCustomToolRestoreSchema = z.object({ version: z.number().int().min(1), expectedVersion: z.number().int().min(1) }).strict();
|
|
91
|
+
export const platformCustomToolSchema = z.object({
|
|
92
|
+
kind: platformCustomToolKindSchema.default("python"), endpoint: platformCustomEndpointSchema.nullable().default(null),
|
|
93
|
+
name: platformCustomToolNameSchema, slug: platformCustomToolSlugSchema, title: z.string(), description: z.string(), inputSchema: platformJsonObjectSchema,
|
|
94
|
+
code: z.string(), timeoutMs: z.number().int(), enabled: z.boolean(), version: z.number().int().min(1), createdAt: z.string(), updatedAt: z.string(),
|
|
95
|
+
});
|
|
96
|
+
export const platformCustomToolSummarySchema = platformCustomToolSchema.omit({ code: true, inputSchema: true });
|
|
97
|
+
export const platformCustomToolVersionSchema = z.object({ version: z.number().int().min(1), title: z.string(), createdAt: z.string() });
|
|
98
|
+
export const platformVersionAuthorSchema = z.object({ email: z.string().nullable(), apiKey: z.string().nullable() }).strict();
|
|
99
|
+
export const platformCustomToolHistorySchema = platformCustomToolVersionSchema.extend({ author: platformVersionAuthorSchema });
|
|
100
|
+
export const platformCustomToolVersionDetailSchema = platformCustomToolSchema.pick({ kind: true, endpoint: true, title: true, description: true, inputSchema: true, code: true, timeoutMs: true, enabled: true })
|
|
101
|
+
.extend({ version: z.number().int().min(1), createdAt: z.string(), author: platformVersionAuthorSchema });
|
|
102
|
+
const toolInput = projectScopeSchema.extend({ name: platformCustomToolNameSchema });
|
|
103
|
+
const toolResult = z.object({ tool: platformCustomToolSchema }).strict();
|
|
104
|
+
export const platformCustomToolContracts = {
|
|
105
|
+
listDeveloperCustomTools: { input: projectScopeSchema.strict(), output: z.object({ tools: z.array(platformCustomToolSummarySchema) }).strict() },
|
|
106
|
+
getDeveloperCustomTool: { input: toolInput.strict(), output: toolResult },
|
|
107
|
+
createDeveloperCustomTool: { input: projectScopeSchema.extend({ tool: platformCustomToolCreateSchema }).strict(), output: toolResult },
|
|
108
|
+
updateDeveloperCustomTool: { input: toolInput.extend({ tool: platformCustomToolUpdateSchema }).strict(), output: toolResult },
|
|
109
|
+
restoreDeveloperCustomTool: { input: toolInput.merge(platformCustomToolRestoreSchema).strict(), output: toolResult },
|
|
110
|
+
deleteDeveloperCustomTool: { input: toolInput.strict(), output: z.object({ success: z.literal(true) }).strict() },
|
|
111
|
+
listDeveloperCustomToolVersions: { input: toolInput.strict(), output: z.object({ versions: z.array(platformCustomToolHistorySchema) }).strict() },
|
|
112
|
+
getDeveloperCustomToolVersion: { input: toolInput.extend({ version: z.number().int().min(1) }).strict(), output: z.object({ version: platformCustomToolVersionDetailSchema }).strict() },
|
|
113
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PLATFORM_CHECKOUT_MIN_CENTS } from "./platformBillingConstants.js";
|
|
3
|
+
const micros = z.string().regex(/^\d{1,20}$/);
|
|
4
|
+
export const platformFundingShortfallSchema = z.object({
|
|
5
|
+
reason: z.enum(["balance", "organization_monthly_limit", "project_monthly_limit", "organization_daily_limit", "api_key_daily_limit"]),
|
|
6
|
+
requiredMicros: micros,
|
|
7
|
+
availableMicros: micros,
|
|
8
|
+
});
|
|
9
|
+
export const PLATFORM_WALLET_TOP_UP_MIN_CENTS = PLATFORM_CHECKOUT_MIN_CENTS;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ACCOUNT_REQUEST_HTTP_METHODS } from "./accountRequestMethods.js";
|
|
3
|
+
export const PLATFORM_APP_HOST_PATTERN = /^(?=[^:]{4,253}(?::|$))(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9](?::[1-9][0-9]{0,4})?$/;
|
|
4
|
+
export const PLATFORM_APP_RESERVED_HEADERS = new Set(["host", "cookie", "content-length", "transfer-encoding", "connection", "proxy-authorization", "forwarded", "te", "upgrade", "expect"]);
|
|
5
|
+
export const platformAppApiPathSchema = z.object({ baseUrl: z.number().int().min(0).max(8), method: z.enum(ACCOUNT_REQUEST_HTTP_METHODS),
|
|
6
|
+
path: z.string().startsWith("/").max(2048).refine(path => !/[?#\\\s\u0000-\u001f]/.test(path) && !path.includes("//")
|
|
7
|
+
&& path.split("/").every(segment => segment !== "." && segment !== ".." && (!/[{}]/.test(segment) || /^\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(segment)))) }).strict();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const PLATFORM_MCP_DIRECT_TOOLS_MAX = 20;
|
|
2
|
+
export function platformMcpDirectToolNames(list) {
|
|
3
|
+
const names = [...new Set((list ?? "").split(",").map(name => name.trim()).filter(Boolean))];
|
|
4
|
+
if (names.length > PLATFORM_MCP_DIRECT_TOOLS_MAX || names.some(name => !/^[a-z][a-z0-9_]{1,63}$/u.test(name)))
|
|
5
|
+
throw new Error(`Name at most ${PLATFORM_MCP_DIRECT_TOOLS_MAX} catalog tools, separated by commas.`);
|
|
6
|
+
return names;
|
|
7
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { platformCatalogSchema, platformCreateExecutionSchema, platformExecutionIdSchema, platformExecutionStatusSchema, platformMicrosSchema, platformQuoteSchema, platformToolNameSchema, platformToolSessionIdSchema, platformOptionalToolSessionIdSchema, PLATFORM_SANDBOX_LIMITS, platformExecutionSchema, platformExecutionContextSchema, platformRequestIntentSchema } from "./platformPublicExecution.js";
|
|
3
|
-
import {
|
|
2
|
+
import { platformCatalogSchema, platformCreateExecutionSchema, platformExecutionIdSchema, platformExecutionStatusSchema, platformMicrosSchema, platformQuoteSchema, platformToolNameSchema, platformToolSessionIdSchema, platformOptionalToolSessionIdSchema, PLATFORM_SANDBOX_LIMITS, platformExecutionSchema, platformExecutionContextSchema, platformRequestIntentSchema, validatePlatformArgumentIdentifiers } from "./platformPublicExecution.js";
|
|
3
|
+
import { platformAnyConnectionProviderSchema, platformConnectionCatalogSchema } from "./platformConnectionCatalog.js";
|
|
4
4
|
import { toolDiscoveryInputSchema, toolDiscoveryResultSchema } from "./toolDiscovery.js";
|
|
5
|
+
export { PLATFORM_MCP_DIRECT_TOOLS_MAX, platformMcpDirectToolNames } from "./platformMcpTools.js";
|
|
5
6
|
export const PLATFORM_META_LIMITS = { queries: 4, schemas: 12, batch: 50, concurrency: 4, contextBytes: 24_000,
|
|
6
7
|
requestBytes: 1_048_576, requestTimeoutMs: 90_000, sessionLifetimeMs: 86_400_000, activeSessions: 100,
|
|
7
8
|
codeChars: PLATFORM_SANDBOX_LIMITS.code, commandChars: 16_000, sourceExecutions: PLATFORM_SANDBOX_LIMITS.sourceExecutions, timeoutMs: PLATFORM_SANDBOX_LIMITS.timeoutMs };
|
|
@@ -21,15 +22,16 @@ export const platformMetaSchemasSchema = z.object({ ...sessionInput,
|
|
|
21
22
|
export const platformMetaSchemasResultSchema = z.object({ session: platformToolSessionSchema,
|
|
22
23
|
tools: platformCatalogSchema.shape.tools, remaining: z.array(platformToolNameSchema) });
|
|
23
24
|
export const platformMetaConnectionsSchema = z.object({ ...sessionInput,
|
|
24
|
-
providers: z.array(
|
|
25
|
-
.describe("OAuth providers
|
|
25
|
+
providers: z.array(platformAnyConnectionProviderSchema).max(32).default([])
|
|
26
|
+
.describe("Providers to inspect: OAuth providers or connected apps as app:<id> (e.g. app:airtable). Empty means every connection. Data-provider API keys are managed by Relvo.") }).strict();
|
|
26
27
|
export const platformMetaConnectionsResultSchema = z.object({ session: platformToolSessionSchema,
|
|
27
28
|
developerOrganizationId: z.string(), developerProjectId: z.string(),
|
|
28
29
|
manageUrl: z.url(), connections: platformConnectionCatalogSchema.shape.connections,
|
|
29
|
-
total: z.number().int().nonnegative(), providers: z.array(z.object({ provider:
|
|
30
|
+
total: z.number().int().nonnegative(), providers: z.array(z.object({ provider: platformAnyConnectionProviderSchema,
|
|
30
31
|
status: z.enum(["connected", "reauth_required", "not_connected"]) })),
|
|
31
32
|
instruction: z.string() });
|
|
32
|
-
const item = platformCreateExecutionSchema.omit({ sessionId: true }).extend({ idempotencyKey: z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/) }).strict()
|
|
33
|
+
const item = z.object(platformCreateExecutionSchema.shape).omit({ sessionId: true }).extend({ idempotencyKey: z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/) }).strict()
|
|
34
|
+
.superRefine((value, context) => validatePlatformArgumentIdentifiers(value.tool, value.arguments, context));
|
|
33
35
|
export const platformMetaExecuteSchema = z.object({ ...sessionInput,
|
|
34
36
|
maximumChargeMicros: platformMicrosSchema.describe("Accepted total reservation ceiling across this batch, in addition to each accepted quote."),
|
|
35
37
|
tools: z.array(item).min(1).max(PLATFORM_META_LIMITS.batch)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { isCronTooFrequent, isValidCron, parseCron } from "./cronValidator.js";
|
|
3
|
+
import { ScheduleTimeZone } from "./scheduleTimeZone.js";
|
|
4
|
+
export const PLATFORM_MONITOR_INTERVALS = { "15m": 900, "1h": 3600, "1d": 86_400 };
|
|
5
|
+
export const platformMonitorIntervalSchema = z.enum(["15m", "1h", "1d"]);
|
|
6
|
+
export const PLATFORM_MONITOR_DEFAULT_INTERVAL = "1h";
|
|
7
|
+
export const PLATFORM_MONITOR_DEFAULT_TIME_ZONE = "UTC";
|
|
8
|
+
export const PLATFORM_MONITOR_MIN_CRON_MINUTES = 15;
|
|
9
|
+
export const PLATFORM_MONITOR_CRON_CHARS = 100;
|
|
10
|
+
const cronText = z.string().trim().max(PLATFORM_MONITOR_CRON_CHARS)
|
|
11
|
+
.refine(isValidCron, "platform_monitor_cron_invalid")
|
|
12
|
+
.refine(cron => !isCronTooFrequent(cron, PLATFORM_MONITOR_MIN_CRON_MINUTES), "platform_monitor_cron_too_frequent");
|
|
13
|
+
export const platformMonitorCronSchema = z.union([z.literal(""), cronText]);
|
|
14
|
+
export const platformMonitorTimeZoneSchema = z.union([z.literal(""), ScheduleTimeZone]);
|
|
15
|
+
export const normalizeMonitorCron = (cron) => cron.trim().split(/\s+/).map(field => field.toUpperCase()).join(" ");
|
|
16
|
+
export class PlatformMonitorScheduleError extends Error {
|
|
17
|
+
}
|
|
18
|
+
export function resolvePlatformMonitorSchedule(input, after = Date.now()) {
|
|
19
|
+
if (!input.cron) {
|
|
20
|
+
if (input.timeZone)
|
|
21
|
+
throw new PlatformMonitorScheduleError("platform_monitor_time_zone_needs_cron");
|
|
22
|
+
return { interval: input.interval ?? PLATFORM_MONITOR_DEFAULT_INTERVAL };
|
|
23
|
+
}
|
|
24
|
+
if (input.interval)
|
|
25
|
+
throw new PlatformMonitorScheduleError("platform_monitor_schedule_conflict");
|
|
26
|
+
const zone = ScheduleTimeZone.safeParse(input.timeZone || PLATFORM_MONITOR_DEFAULT_TIME_ZONE);
|
|
27
|
+
if (!zone.success)
|
|
28
|
+
throw new PlatformMonitorScheduleError("platform_monitor_time_zone_invalid");
|
|
29
|
+
const cron = normalizeMonitorCron(input.cron), timeZone = zone.data;
|
|
30
|
+
if (nextCronFire(cron, timeZone, after) === null)
|
|
31
|
+
throw new PlatformMonitorScheduleError("platform_monitor_cron_never_runs");
|
|
32
|
+
return { cron, timeZone };
|
|
33
|
+
}
|
|
34
|
+
const MINUTE = 60_000, HOUR = 3_600_000, DAY = 86_400_000;
|
|
35
|
+
const SEARCH_DAYS = 366 * 8;
|
|
36
|
+
const formatters = new Map();
|
|
37
|
+
function offsetAt(timeZone, at) {
|
|
38
|
+
let format = formatters.get(timeZone);
|
|
39
|
+
if (!format) {
|
|
40
|
+
format = new Intl.DateTimeFormat("en-US", { timeZone, hourCycle: "h23", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric" });
|
|
41
|
+
formatters.set(timeZone, format);
|
|
42
|
+
}
|
|
43
|
+
const part = {};
|
|
44
|
+
for (const { type, value } of format.formatToParts(at))
|
|
45
|
+
if (type !== "literal")
|
|
46
|
+
part[type] = Number(value);
|
|
47
|
+
return Date.UTC(part.year, part.month - 1, part.day, part.hour, part.minute, part.second) - Math.floor(at / 1000) * 1000;
|
|
48
|
+
}
|
|
49
|
+
function instantOf(timeZone, wall) {
|
|
50
|
+
const before = offsetAt(timeZone, wall - DAY), after = offsetAt(timeZone, wall + DAY);
|
|
51
|
+
const candidates = [...new Set([wall - before, wall - after])].sort((a, b) => a - b);
|
|
52
|
+
const valid = candidates.filter(at => offsetAt(timeZone, at) === wall - at);
|
|
53
|
+
if (valid.length)
|
|
54
|
+
return valid[0];
|
|
55
|
+
let low = candidates[0], high = candidates[candidates.length - 1];
|
|
56
|
+
if (low === high)
|
|
57
|
+
return low;
|
|
58
|
+
const lowOffset = offsetAt(timeZone, low);
|
|
59
|
+
while (high - low > MINUTE) {
|
|
60
|
+
const mid = low + Math.floor((high - low) / (2 * MINUTE)) * MINUTE;
|
|
61
|
+
if (offsetAt(timeZone, mid) === lowOffset)
|
|
62
|
+
low = mid;
|
|
63
|
+
else
|
|
64
|
+
high = mid;
|
|
65
|
+
}
|
|
66
|
+
return high;
|
|
67
|
+
}
|
|
68
|
+
const dayMatches = (parsed, year, month, day) => {
|
|
69
|
+
if (!parsed.months.has(month))
|
|
70
|
+
return false;
|
|
71
|
+
const dom = parsed.daysOfMonth.has(day), dow = parsed.daysOfWeek.has(new Date(Date.UTC(year, month - 1, day)).getUTCDay());
|
|
72
|
+
return parsed.dayRestrictionOr ? dom || dow : dom && dow;
|
|
73
|
+
};
|
|
74
|
+
const ascending = (values) => [...values].sort((a, b) => a - b);
|
|
75
|
+
export function nextCronFire(cron, timeZone, after) {
|
|
76
|
+
const parsed = parseCron(cron);
|
|
77
|
+
if (!parsed)
|
|
78
|
+
return null;
|
|
79
|
+
const hours = ascending(parsed.hours), minutes = ascending(parsed.minutes);
|
|
80
|
+
const wallAfter = after + offsetAt(timeZone, after);
|
|
81
|
+
const first = new Date(wallAfter);
|
|
82
|
+
const start = Date.UTC(first.getUTCFullYear(), first.getUTCMonth(), first.getUTCDate()) - DAY;
|
|
83
|
+
for (let index = 0; index < SEARCH_DAYS; index++) {
|
|
84
|
+
const dayStart = start + index * DAY, date = new Date(dayStart);
|
|
85
|
+
if (!dayMatches(parsed, date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()))
|
|
86
|
+
continue;
|
|
87
|
+
for (const hour of hours)
|
|
88
|
+
for (const minute of minutes) {
|
|
89
|
+
const wall = dayStart + hour * HOUR + minute * MINUTE;
|
|
90
|
+
if (wall < wallAfter - 6 * HOUR)
|
|
91
|
+
continue;
|
|
92
|
+
const at = instantOf(timeZone, wall);
|
|
93
|
+
if (at > after)
|
|
94
|
+
return at;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
export function upcomingCronFires(cron, timeZone, after, count) {
|
|
100
|
+
const fires = [];
|
|
101
|
+
for (let at = after; fires.length < count;) {
|
|
102
|
+
const next = nextCronFire(cron, timeZone, at);
|
|
103
|
+
if (next === null)
|
|
104
|
+
break;
|
|
105
|
+
fires.push(next);
|
|
106
|
+
at = next;
|
|
107
|
+
}
|
|
108
|
+
return fires;
|
|
109
|
+
}
|
|
110
|
+
export function monitorRunsPerMonth(schedule) {
|
|
111
|
+
if (!("cron" in schedule))
|
|
112
|
+
return Math.round((365 / 12) * 86_400 / PLATFORM_MONITOR_INTERVALS[schedule.interval]);
|
|
113
|
+
const parsed = parseCron(schedule.cron);
|
|
114
|
+
if (!parsed)
|
|
115
|
+
return 0;
|
|
116
|
+
let days = 0;
|
|
117
|
+
for (let index = 0; index < 365; index++) {
|
|
118
|
+
const date = new Date(Date.UTC(2026, 0, 1 + index));
|
|
119
|
+
if (dayMatches(parsed, date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()))
|
|
120
|
+
days++;
|
|
121
|
+
}
|
|
122
|
+
return Math.round(days * parsed.hours.size * parsed.minutes.size / 12);
|
|
123
|
+
}
|