intentkit 0.7.5.dev3__py3-none-any.whl → 0.8.34.dev7__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.
- intentkit/MANIFEST.in +14 -0
- intentkit/README.md +88 -0
- intentkit/__init__.py +6 -4
- intentkit/abstracts/agent.py +4 -5
- intentkit/abstracts/engine.py +5 -5
- intentkit/abstracts/graph.py +15 -8
- intentkit/abstracts/skill.py +6 -144
- intentkit/abstracts/twitter.py +4 -5
- intentkit/clients/__init__.py +9 -2
- intentkit/clients/cdp.py +129 -153
- intentkit/{utils → clients}/s3.py +109 -34
- intentkit/clients/twitter.py +83 -62
- intentkit/clients/web3.py +4 -7
- intentkit/config/config.py +123 -90
- intentkit/core/account_checking.py +802 -0
- intentkit/core/agent.py +313 -498
- intentkit/core/asset.py +267 -0
- intentkit/core/chat.py +5 -3
- intentkit/core/client.py +1 -1
- intentkit/core/credit.py +49 -41
- intentkit/core/draft.py +201 -0
- intentkit/core/draft_chat.py +118 -0
- intentkit/core/engine.py +378 -287
- intentkit/core/manager/__init__.py +25 -0
- intentkit/core/manager/engine.py +220 -0
- intentkit/core/manager/service.py +172 -0
- intentkit/core/manager/skills.py +178 -0
- intentkit/core/middleware.py +231 -0
- intentkit/core/prompt.py +74 -114
- intentkit/core/scheduler.py +143 -0
- intentkit/core/statistics.py +168 -0
- intentkit/models/agent.py +931 -518
- intentkit/models/agent_data.py +165 -106
- intentkit/models/agent_schema.json +38 -251
- intentkit/models/app_setting.py +15 -13
- intentkit/models/chat.py +86 -140
- intentkit/models/credit.py +182 -162
- intentkit/models/db.py +42 -23
- intentkit/models/db_mig.py +120 -3
- intentkit/models/draft.py +222 -0
- intentkit/models/llm.csv +31 -0
- intentkit/models/llm.py +262 -370
- intentkit/models/redis.py +6 -4
- intentkit/models/skill.py +222 -101
- intentkit/models/skills.csv +173 -0
- intentkit/models/team.py +189 -0
- intentkit/models/user.py +103 -31
- intentkit/skills/acolyt/__init__.py +2 -9
- intentkit/skills/acolyt/ask.py +3 -4
- intentkit/skills/acolyt/base.py +4 -9
- intentkit/skills/acolyt/schema.json +4 -3
- intentkit/skills/aixbt/__init__.py +2 -13
- intentkit/skills/aixbt/base.py +1 -7
- intentkit/skills/aixbt/projects.py +14 -15
- intentkit/skills/aixbt/schema.json +4 -4
- intentkit/skills/allora/__init__.py +2 -9
- intentkit/skills/allora/base.py +4 -9
- intentkit/skills/allora/price.py +3 -4
- intentkit/skills/allora/schema.json +3 -2
- intentkit/skills/base.py +241 -41
- intentkit/skills/basename/__init__.py +51 -0
- intentkit/skills/basename/base.py +11 -0
- intentkit/skills/basename/basename.svg +11 -0
- intentkit/skills/basename/schema.json +58 -0
- intentkit/skills/carv/__init__.py +115 -121
- intentkit/skills/carv/base.py +184 -185
- intentkit/skills/carv/fetch_news.py +3 -3
- intentkit/skills/carv/onchain_query.py +4 -4
- intentkit/skills/carv/schema.json +134 -137
- intentkit/skills/carv/token_info_and_price.py +6 -6
- intentkit/skills/casino/__init__.py +4 -15
- intentkit/skills/casino/base.py +1 -7
- intentkit/skills/casino/deck_draw.py +5 -8
- intentkit/skills/casino/deck_shuffle.py +6 -6
- intentkit/skills/casino/dice_roll.py +2 -4
- intentkit/skills/casino/schema.json +0 -1
- intentkit/skills/cdp/__init__.py +22 -84
- intentkit/skills/cdp/base.py +1 -7
- intentkit/skills/cdp/schema.json +11 -314
- intentkit/skills/chainlist/__init__.py +2 -7
- intentkit/skills/chainlist/base.py +1 -7
- intentkit/skills/chainlist/chain_lookup.py +18 -18
- intentkit/skills/chainlist/schema.json +3 -5
- intentkit/skills/common/__init__.py +2 -9
- intentkit/skills/common/base.py +1 -7
- intentkit/skills/common/current_time.py +1 -2
- intentkit/skills/common/schema.json +2 -2
- intentkit/skills/cookiefun/__init__.py +6 -9
- intentkit/skills/cookiefun/base.py +2 -7
- intentkit/skills/cookiefun/get_account_details.py +7 -7
- intentkit/skills/cookiefun/get_account_feed.py +19 -19
- intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
- intentkit/skills/cookiefun/get_sectors.py +3 -3
- intentkit/skills/cookiefun/schema.json +1 -3
- intentkit/skills/cookiefun/search_accounts.py +9 -9
- intentkit/skills/cryptocompare/__init__.py +7 -24
- intentkit/skills/cryptocompare/api.py +2 -3
- intentkit/skills/cryptocompare/base.py +10 -24
- intentkit/skills/cryptocompare/fetch_news.py +4 -5
- intentkit/skills/cryptocompare/fetch_price.py +6 -7
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
- intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
- intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
- intentkit/skills/cryptocompare/schema.json +3 -3
- intentkit/skills/cryptopanic/__init__.py +7 -10
- intentkit/skills/cryptopanic/base.py +51 -55
- intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
- intentkit/skills/cryptopanic/schema.json +105 -103
- intentkit/skills/dapplooker/__init__.py +2 -9
- intentkit/skills/dapplooker/base.py +4 -9
- intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
- intentkit/skills/dapplooker/schema.json +3 -5
- intentkit/skills/defillama/__init__.py +24 -74
- intentkit/skills/defillama/api.py +6 -9
- intentkit/skills/defillama/base.py +8 -19
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
- intentkit/skills/defillama/coins/fetch_block.py +6 -8
- intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
- intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
- intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
- intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
- intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
- intentkit/skills/defillama/config/chains.py +1 -3
- intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
- intentkit/skills/defillama/schema.json +5 -1
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
- intentkit/skills/defillama/tests/api_integration.test.py +1 -1
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
- intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
- intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
- intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
- intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
- intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
- intentkit/skills/defillama/yields/fetch_pools.py +26 -30
- intentkit/skills/dexscreener/__init__.py +97 -102
- intentkit/skills/dexscreener/base.py +125 -130
- intentkit/skills/dexscreener/get_pair_info.py +4 -5
- intentkit/skills/dexscreener/get_token_pairs.py +4 -5
- intentkit/skills/dexscreener/get_tokens_info.py +7 -8
- intentkit/skills/dexscreener/model/search_token_response.py +80 -82
- intentkit/skills/dexscreener/schema.json +91 -93
- intentkit/skills/dexscreener/search_token.py +182 -184
- intentkit/skills/dexscreener/utils.py +15 -14
- intentkit/skills/dune_analytics/__init__.py +7 -9
- intentkit/skills/dune_analytics/base.py +48 -52
- intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
- intentkit/skills/dune_analytics/schema.json +104 -99
- intentkit/skills/elfa/__init__.py +5 -18
- intentkit/skills/elfa/base.py +10 -14
- intentkit/skills/elfa/mention.py +19 -21
- intentkit/skills/elfa/schema.json +3 -2
- intentkit/skills/elfa/stats.py +4 -4
- intentkit/skills/elfa/tokens.py +12 -12
- intentkit/skills/elfa/utils.py +26 -28
- intentkit/skills/enso/__init__.py +11 -31
- intentkit/skills/enso/base.py +54 -35
- intentkit/skills/enso/best_yield.py +16 -24
- intentkit/skills/enso/networks.py +6 -11
- intentkit/skills/enso/prices.py +11 -13
- intentkit/skills/enso/route.py +34 -38
- intentkit/skills/enso/schema.json +3 -2
- intentkit/skills/enso/tokens.py +29 -38
- intentkit/skills/enso/wallet.py +76 -191
- intentkit/skills/erc20/__init__.py +50 -0
- intentkit/skills/erc20/base.py +11 -0
- intentkit/skills/erc20/erc20.svg +5 -0
- intentkit/skills/erc20/schema.json +74 -0
- intentkit/skills/erc721/__init__.py +53 -0
- intentkit/skills/erc721/base.py +11 -0
- intentkit/skills/erc721/erc721.svg +5 -0
- intentkit/skills/erc721/schema.json +90 -0
- intentkit/skills/firecrawl/__init__.py +5 -18
- intentkit/skills/firecrawl/base.py +4 -9
- intentkit/skills/firecrawl/clear.py +4 -8
- intentkit/skills/firecrawl/crawl.py +19 -19
- intentkit/skills/firecrawl/query.py +4 -3
- intentkit/skills/firecrawl/schema.json +2 -6
- intentkit/skills/firecrawl/scrape.py +17 -22
- intentkit/skills/firecrawl/utils.py +50 -42
- intentkit/skills/github/__init__.py +2 -7
- intentkit/skills/github/base.py +1 -7
- intentkit/skills/github/github_search.py +1 -2
- intentkit/skills/github/schema.json +3 -4
- intentkit/skills/heurist/__init__.py +8 -27
- intentkit/skills/heurist/base.py +4 -9
- intentkit/skills/heurist/image_generation_animagine_xl.py +13 -15
- intentkit/skills/heurist/image_generation_arthemy_comics.py +13 -15
- intentkit/skills/heurist/image_generation_arthemy_real.py +13 -15
- intentkit/skills/heurist/image_generation_braindance.py +13 -15
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +13 -15
- intentkit/skills/heurist/image_generation_flux_1_dev.py +13 -15
- intentkit/skills/heurist/image_generation_sdxl.py +13 -15
- intentkit/skills/heurist/schema.json +2 -2
- intentkit/skills/http/__init__.py +4 -15
- intentkit/skills/http/base.py +1 -7
- intentkit/skills/http/get.py +21 -16
- intentkit/skills/http/post.py +23 -18
- intentkit/skills/http/put.py +23 -18
- intentkit/skills/http/schema.json +4 -5
- intentkit/skills/lifi/__init__.py +8 -13
- intentkit/skills/lifi/base.py +3 -9
- intentkit/skills/lifi/schema.json +17 -8
- intentkit/skills/lifi/token_execute.py +150 -60
- intentkit/skills/lifi/token_quote.py +8 -10
- intentkit/skills/lifi/utils.py +104 -51
- intentkit/skills/moralis/__init__.py +6 -10
- intentkit/skills/moralis/api.py +6 -7
- intentkit/skills/moralis/base.py +5 -10
- intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
- intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
- intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
- intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
- intentkit/skills/moralis/schema.json +7 -2
- intentkit/skills/morpho/__init__.py +52 -0
- intentkit/skills/morpho/base.py +11 -0
- intentkit/skills/morpho/morpho.svg +12 -0
- intentkit/skills/morpho/schema.json +73 -0
- intentkit/skills/nation/__init__.py +4 -9
- intentkit/skills/nation/base.py +5 -10
- intentkit/skills/nation/nft_check.py +3 -4
- intentkit/skills/nation/schema.json +4 -3
- intentkit/skills/onchain.py +30 -0
- intentkit/skills/openai/__init__.py +17 -18
- intentkit/skills/openai/base.py +10 -14
- intentkit/skills/openai/dalle_image_generation.py +4 -9
- intentkit/skills/openai/gpt_avatar_generator.py +102 -0
- intentkit/skills/openai/gpt_image_generation.py +5 -9
- intentkit/skills/openai/gpt_image_mini_generator.py +92 -0
- intentkit/skills/openai/gpt_image_to_image.py +5 -9
- intentkit/skills/openai/image_to_text.py +3 -7
- intentkit/skills/openai/schema.json +34 -3
- intentkit/skills/portfolio/__init__.py +11 -35
- intentkit/skills/portfolio/base.py +33 -19
- intentkit/skills/portfolio/schema.json +3 -5
- intentkit/skills/portfolio/token_balances.py +21 -21
- intentkit/skills/portfolio/wallet_approvals.py +17 -18
- intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
- intentkit/skills/portfolio/wallet_history.py +31 -31
- intentkit/skills/portfolio/wallet_net_worth.py +13 -13
- intentkit/skills/portfolio/wallet_nfts.py +19 -19
- intentkit/skills/portfolio/wallet_profitability.py +18 -18
- intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
- intentkit/skills/portfolio/wallet_stats.py +3 -3
- intentkit/skills/portfolio/wallet_swaps.py +19 -19
- intentkit/skills/pyth/__init__.py +50 -0
- intentkit/skills/pyth/base.py +11 -0
- intentkit/skills/pyth/pyth.svg +6 -0
- intentkit/skills/pyth/schema.json +75 -0
- intentkit/skills/skills.toml +36 -0
- intentkit/skills/slack/__init__.py +5 -17
- intentkit/skills/slack/base.py +3 -9
- intentkit/skills/slack/get_channel.py +8 -8
- intentkit/skills/slack/get_message.py +9 -9
- intentkit/skills/slack/schedule_message.py +5 -5
- intentkit/skills/slack/schema.json +2 -2
- intentkit/skills/slack/send_message.py +3 -5
- intentkit/skills/supabase/__init__.py +7 -23
- intentkit/skills/supabase/base.py +1 -7
- intentkit/skills/supabase/delete_data.py +4 -4
- intentkit/skills/supabase/fetch_data.py +12 -12
- intentkit/skills/supabase/insert_data.py +4 -4
- intentkit/skills/supabase/invoke_function.py +6 -6
- intentkit/skills/supabase/schema.json +2 -3
- intentkit/skills/supabase/update_data.py +6 -6
- intentkit/skills/supabase/upsert_data.py +4 -4
- intentkit/skills/superfluid/__init__.py +53 -0
- intentkit/skills/superfluid/base.py +11 -0
- intentkit/skills/superfluid/schema.json +89 -0
- intentkit/skills/superfluid/superfluid.svg +6 -0
- intentkit/skills/system/__init__.py +7 -24
- intentkit/skills/system/add_autonomous_task.py +10 -12
- intentkit/skills/system/delete_autonomous_task.py +2 -2
- intentkit/skills/system/edit_autonomous_task.py +14 -18
- intentkit/skills/system/list_autonomous_tasks.py +3 -5
- intentkit/skills/system/read_agent_api_key.py +6 -4
- intentkit/skills/system/regenerate_agent_api_key.py +6 -4
- intentkit/skills/system/schema.json +6 -8
- intentkit/skills/tavily/__init__.py +3 -12
- intentkit/skills/tavily/base.py +4 -9
- intentkit/skills/tavily/schema.json +3 -5
- intentkit/skills/tavily/tavily_extract.py +2 -4
- intentkit/skills/tavily/tavily_search.py +4 -6
- intentkit/skills/token/__init__.py +5 -10
- intentkit/skills/token/base.py +7 -11
- intentkit/skills/token/erc20_transfers.py +19 -19
- intentkit/skills/token/schema.json +3 -6
- intentkit/skills/token/token_analytics.py +3 -3
- intentkit/skills/token/token_price.py +13 -13
- intentkit/skills/token/token_search.py +9 -9
- intentkit/skills/twitter/__init__.py +11 -35
- intentkit/skills/twitter/base.py +22 -34
- intentkit/skills/twitter/follow_user.py +2 -6
- intentkit/skills/twitter/get_mentions.py +5 -12
- intentkit/skills/twitter/get_timeline.py +4 -12
- intentkit/skills/twitter/get_user_by_username.py +2 -6
- intentkit/skills/twitter/get_user_tweets.py +5 -13
- intentkit/skills/twitter/like_tweet.py +2 -6
- intentkit/skills/twitter/post_tweet.py +6 -9
- intentkit/skills/twitter/reply_tweet.py +6 -9
- intentkit/skills/twitter/retweet.py +2 -6
- intentkit/skills/twitter/schema.json +1 -0
- intentkit/skills/twitter/search_tweets.py +4 -12
- intentkit/skills/unrealspeech/__init__.py +2 -7
- intentkit/skills/unrealspeech/base.py +2 -8
- intentkit/skills/unrealspeech/schema.json +2 -5
- intentkit/skills/unrealspeech/text_to_speech.py +8 -8
- intentkit/skills/venice_audio/__init__.py +98 -106
- intentkit/skills/venice_audio/base.py +117 -121
- intentkit/skills/venice_audio/input.py +41 -41
- intentkit/skills/venice_audio/schema.json +151 -152
- intentkit/skills/venice_audio/venice_audio.py +38 -21
- intentkit/skills/venice_image/__init__.py +147 -154
- intentkit/skills/venice_image/api.py +138 -138
- intentkit/skills/venice_image/base.py +185 -192
- intentkit/skills/venice_image/config.py +33 -35
- intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
- intentkit/skills/venice_image/image_generation/image_generation_base.py +11 -10
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
- intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
- intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
- intentkit/skills/venice_image/schema.json +267 -267
- intentkit/skills/venice_image/utils.py +77 -78
- intentkit/skills/web_scraper/__init__.py +5 -18
- intentkit/skills/web_scraper/base.py +21 -7
- intentkit/skills/web_scraper/document_indexer.py +7 -6
- intentkit/skills/web_scraper/schema.json +2 -6
- intentkit/skills/web_scraper/scrape_and_index.py +15 -15
- intentkit/skills/web_scraper/utils.py +62 -63
- intentkit/skills/web_scraper/website_indexer.py +17 -19
- intentkit/skills/weth/__init__.py +49 -0
- intentkit/skills/weth/base.py +11 -0
- intentkit/skills/weth/schema.json +58 -0
- intentkit/skills/weth/weth.svg +6 -0
- intentkit/skills/wow/__init__.py +51 -0
- intentkit/skills/wow/base.py +11 -0
- intentkit/skills/wow/schema.json +89 -0
- intentkit/skills/wow/wow.svg +7 -0
- intentkit/skills/x402/__init__.py +58 -0
- intentkit/skills/x402/base.py +99 -0
- intentkit/skills/x402/http_request.py +117 -0
- intentkit/skills/x402/schema.json +40 -0
- intentkit/skills/x402/x402.webp +0 -0
- intentkit/skills/xmtp/__init__.py +4 -15
- intentkit/skills/xmtp/base.py +5 -5
- intentkit/skills/xmtp/price.py +7 -6
- intentkit/skills/xmtp/schema.json +69 -71
- intentkit/skills/xmtp/swap.py +6 -8
- intentkit/skills/xmtp/transfer.py +4 -6
- intentkit/utils/__init__.py +4 -0
- intentkit/utils/chain.py +198 -96
- intentkit/utils/ens.py +135 -0
- intentkit/utils/error.py +5 -2
- intentkit/utils/logging.py +9 -11
- intentkit/utils/schema.py +100 -0
- intentkit/utils/slack_alert.py +8 -8
- intentkit/utils/tx.py +16 -8
- intentkit/uv.lock +3377 -0
- {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/METADATA +13 -15
- intentkit-0.8.34.dev7.dist-info/RECORD +478 -0
- intentkit-0.8.34.dev7.dist-info/licenses/LICENSE +21 -0
- intentkit/core/node.py +0 -215
- intentkit/models/conversation.py +0 -286
- intentkit/models/generator.py +0 -347
- intentkit/skills/cdp/get_balance.py +0 -110
- intentkit/skills/cdp/swap.py +0 -121
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +0 -511
- intentkit-0.7.5.dev3.dist-info/RECORD +0 -424
- {intentkit-0.7.5.dev3.dist-info/licenses → intentkit}/LICENSE +0 -0
- {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/WHEEL +0 -0
intentkit/skills/base.py
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Callable, Sequence
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Literal,
|
|
6
|
+
NotRequired,
|
|
7
|
+
TypedDict,
|
|
8
|
+
)
|
|
3
9
|
|
|
4
|
-
from
|
|
10
|
+
from coinbase_agentkit import (
|
|
11
|
+
Action,
|
|
12
|
+
AgentKit,
|
|
13
|
+
AgentKitConfig,
|
|
14
|
+
CdpEvmWalletProvider,
|
|
15
|
+
)
|
|
16
|
+
from langchain_core.tools import BaseTool, StructuredTool
|
|
5
17
|
from langchain_core.tools.base import ToolException
|
|
6
18
|
from langgraph.runtime import get_runtime
|
|
7
19
|
from pydantic import (
|
|
@@ -9,13 +21,18 @@ from pydantic import (
|
|
|
9
21
|
)
|
|
10
22
|
from pydantic.v1 import ValidationError as ValidationErrorV1
|
|
11
23
|
from redis.exceptions import RedisError
|
|
12
|
-
from web3 import Web3
|
|
13
24
|
|
|
14
25
|
from intentkit.abstracts.graph import AgentContext
|
|
15
|
-
from intentkit.
|
|
16
|
-
from intentkit.
|
|
26
|
+
from intentkit.clients import get_wallet_provider
|
|
27
|
+
from intentkit.models.agent import Agent
|
|
17
28
|
from intentkit.models.redis import get_redis
|
|
18
|
-
from intentkit.
|
|
29
|
+
from intentkit.models.skill import (
|
|
30
|
+
AgentSkillData,
|
|
31
|
+
AgentSkillDataCreate,
|
|
32
|
+
ChatSkillData,
|
|
33
|
+
ChatSkillDataCreate,
|
|
34
|
+
)
|
|
35
|
+
from intentkit.utils.error import IntentKitAPIError, RateLimitExceeded
|
|
19
36
|
|
|
20
37
|
SkillState = Literal["disabled", "public", "private"]
|
|
21
38
|
SkillOwnerState = Literal["disabled", "private"]
|
|
@@ -26,9 +43,9 @@ class SkillConfig(TypedDict):
|
|
|
26
43
|
"""Abstract base class for skill configuration."""
|
|
27
44
|
|
|
28
45
|
enabled: bool
|
|
29
|
-
states:
|
|
46
|
+
states: dict[str, SkillState | SkillOwnerState]
|
|
30
47
|
api_key_provider: NotRequired[APIKeyProviderValue]
|
|
31
|
-
__extra__: NotRequired[
|
|
48
|
+
__extra__: NotRequired[dict[str, Any]]
|
|
32
49
|
|
|
33
50
|
|
|
34
51
|
class IntentKitSkill(BaseTool):
|
|
@@ -36,17 +53,16 @@ class IntentKitSkill(BaseTool):
|
|
|
36
53
|
Will have predefined abilities.
|
|
37
54
|
"""
|
|
38
55
|
|
|
39
|
-
skill_store: SkillStoreABC
|
|
40
56
|
# overwrite the value of BaseTool
|
|
41
|
-
handle_tool_error:
|
|
57
|
+
handle_tool_error: bool | str | Callable[[ToolException], str] | None = (
|
|
42
58
|
lambda e: f"tool error: {e}"
|
|
43
59
|
)
|
|
44
60
|
"""Handle the content of the ToolException thrown."""
|
|
45
61
|
|
|
46
62
|
# overwrite the value of BaseTool
|
|
47
|
-
handle_validation_error:
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
handle_validation_error: (
|
|
64
|
+
bool | str | Callable[[ValidationError | ValidationErrorV1], str] | None
|
|
65
|
+
) = lambda e: f"validation error: {e}"
|
|
50
66
|
"""Handle the content of the ValidationError thrown."""
|
|
51
67
|
|
|
52
68
|
# Logger for the class
|
|
@@ -57,15 +73,12 @@ class IntentKitSkill(BaseTool):
|
|
|
57
73
|
"""Get the category of the skill."""
|
|
58
74
|
raise NotImplementedError
|
|
59
75
|
|
|
60
|
-
async def user_rate_limit(
|
|
61
|
-
self, user_id: str, limit: int, minutes: int, key: str
|
|
62
|
-
) -> None:
|
|
76
|
+
async def user_rate_limit(self, limit: int, seconds: int, key: str) -> None:
|
|
63
77
|
"""Check if a user has exceeded the rate limit for this skill.
|
|
64
78
|
|
|
65
79
|
Args:
|
|
66
|
-
user_id: The ID of the user to check
|
|
67
80
|
limit: Maximum number of requests allowed
|
|
68
|
-
|
|
81
|
+
seconds: Time window in seconds
|
|
69
82
|
key: The key to use for rate limiting (e.g., skill name or category)
|
|
70
83
|
|
|
71
84
|
Raises:
|
|
@@ -74,25 +87,48 @@ class IntentKitSkill(BaseTool):
|
|
|
74
87
|
Returns:
|
|
75
88
|
None: Always returns None if no exception is raised
|
|
76
89
|
"""
|
|
77
|
-
|
|
78
|
-
|
|
90
|
+
try:
|
|
91
|
+
context = self.get_context()
|
|
92
|
+
except ValueError:
|
|
93
|
+
self.logger.info(
|
|
94
|
+
"AgentContext not available, skipping rate limit for %s",
|
|
95
|
+
key,
|
|
96
|
+
)
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
user_identifier = context.user_id or context.agent_id
|
|
100
|
+
if not user_identifier:
|
|
101
|
+
return None # No rate limiting when no identifier is available
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
max_requests = int(limit)
|
|
105
|
+
window_seconds = int(seconds)
|
|
106
|
+
except (TypeError, ValueError):
|
|
107
|
+
self.logger.info(
|
|
108
|
+
"Invalid user rate limit parameters for %s: limit=%r, seconds=%r",
|
|
109
|
+
key,
|
|
110
|
+
limit,
|
|
111
|
+
seconds,
|
|
112
|
+
)
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
if window_seconds <= 0 or max_requests <= 0:
|
|
116
|
+
return None
|
|
79
117
|
|
|
80
118
|
try:
|
|
81
119
|
redis = get_redis()
|
|
82
120
|
# Create a unique key for this rate limit and user
|
|
83
|
-
rate_limit_key = f"rate_limit:{key}:{
|
|
121
|
+
rate_limit_key = f"rate_limit:{key}:{user_identifier}"
|
|
84
122
|
|
|
85
123
|
# Get the current count
|
|
86
124
|
count = await redis.incr(rate_limit_key)
|
|
87
125
|
|
|
88
126
|
# Set expiration if this is the first request
|
|
89
127
|
if count == 1:
|
|
90
|
-
await redis.expire(
|
|
91
|
-
rate_limit_key, minutes * 60
|
|
92
|
-
) # Convert minutes to seconds
|
|
128
|
+
await redis.expire(rate_limit_key, window_seconds)
|
|
93
129
|
|
|
94
130
|
# Check if user has exceeded the limit
|
|
95
|
-
if count >
|
|
131
|
+
if count > max_requests:
|
|
96
132
|
raise RateLimitExceeded(f"Rate limit exceeded for {key}")
|
|
97
133
|
|
|
98
134
|
return None
|
|
@@ -108,40 +144,97 @@ class IntentKitSkill(BaseTool):
|
|
|
108
144
|
)
|
|
109
145
|
return None
|
|
110
146
|
|
|
111
|
-
async def user_rate_limit_by_skill(
|
|
112
|
-
self, user_id: str, limit: int, minutes: int
|
|
113
|
-
) -> None:
|
|
147
|
+
async def user_rate_limit_by_skill(self, limit: int, seconds: int) -> None:
|
|
114
148
|
"""Check if a user has exceeded the rate limit for this specific skill.
|
|
115
149
|
|
|
116
150
|
This uses the skill name as the rate limit key.
|
|
117
151
|
|
|
118
152
|
Args:
|
|
119
|
-
user_id: The ID of the user to check
|
|
120
153
|
limit: Maximum number of requests allowed
|
|
121
|
-
|
|
154
|
+
seconds: Time window in seconds
|
|
122
155
|
|
|
123
156
|
Raises:
|
|
124
157
|
RateLimitExceeded: If the user has exceeded the rate limit
|
|
125
158
|
"""
|
|
126
|
-
return await self.user_rate_limit(
|
|
159
|
+
return await self.user_rate_limit(limit, seconds, self.name)
|
|
127
160
|
|
|
128
|
-
async def user_rate_limit_by_category(
|
|
129
|
-
self, user_id: str, limit: int, minutes: int
|
|
130
|
-
) -> None:
|
|
161
|
+
async def user_rate_limit_by_category(self, limit: int, seconds: int) -> None:
|
|
131
162
|
"""Check if a user has exceeded the rate limit for this skill category.
|
|
132
163
|
|
|
133
164
|
This uses the skill category as the rate limit key, which means the limit
|
|
134
165
|
is shared across all skills in the same category.
|
|
135
166
|
|
|
136
167
|
Args:
|
|
137
|
-
user_id: The ID of the user to check
|
|
138
168
|
limit: Maximum number of requests allowed
|
|
139
|
-
|
|
169
|
+
seconds: Time window in seconds
|
|
140
170
|
|
|
141
171
|
Raises:
|
|
142
172
|
RateLimitExceeded: If the user has exceeded the rate limit
|
|
143
173
|
"""
|
|
144
|
-
return await self.user_rate_limit(
|
|
174
|
+
return await self.user_rate_limit(limit, seconds, self.category)
|
|
175
|
+
|
|
176
|
+
async def global_rate_limit(self, limit: int, seconds: int, key: str) -> None:
|
|
177
|
+
"""Check if a global rate limit has been exceeded for a given key.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
limit: Maximum number of requests allowed
|
|
181
|
+
seconds: Time window in seconds
|
|
182
|
+
key: The key to use for rate limiting (e.g., skill name or category)
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
RateLimitExceeded: If the global limit has been exceeded
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
None: Always returns None if no exception is raised
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
max_requests = int(limit)
|
|
192
|
+
window_seconds = int(seconds)
|
|
193
|
+
except (TypeError, ValueError):
|
|
194
|
+
self.logger.info(
|
|
195
|
+
"Invalid global rate limit parameters for %s: limit=%r, seconds=%r",
|
|
196
|
+
key,
|
|
197
|
+
limit,
|
|
198
|
+
seconds,
|
|
199
|
+
)
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
if window_seconds <= 0 or max_requests <= 0:
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
redis = get_redis()
|
|
207
|
+
rate_limit_key = f"rate_limit:{key}"
|
|
208
|
+
|
|
209
|
+
count = await redis.incr(rate_limit_key)
|
|
210
|
+
|
|
211
|
+
if count == 1:
|
|
212
|
+
await redis.expire(rate_limit_key, window_seconds)
|
|
213
|
+
|
|
214
|
+
if count > max_requests:
|
|
215
|
+
raise RateLimitExceeded(f"Global rate limit exceeded for {key}")
|
|
216
|
+
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
except RuntimeError:
|
|
220
|
+
self.logger.info(
|
|
221
|
+
"Redis not initialized, skipping global rate limit for %s",
|
|
222
|
+
key,
|
|
223
|
+
)
|
|
224
|
+
return None
|
|
225
|
+
except RedisError as e:
|
|
226
|
+
self.logger.info(
|
|
227
|
+
f"Redis error in global rate limiting: {e}, skipping rate limit for {key}"
|
|
228
|
+
)
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
async def global_rate_limit_by_skill(self, limit: int, seconds: int) -> None:
|
|
232
|
+
"""Apply a global rate limit scoped to this specific skill."""
|
|
233
|
+
return await self.global_rate_limit(limit, seconds, self.name)
|
|
234
|
+
|
|
235
|
+
async def global_rate_limit_by_category(self, limit: int, seconds: int) -> None:
|
|
236
|
+
"""Apply a global rate limit scoped to this skill category."""
|
|
237
|
+
return await self.global_rate_limit(limit, seconds, self.category)
|
|
145
238
|
|
|
146
239
|
def _run(self, *args: Any, **kwargs: Any) -> Any:
|
|
147
240
|
raise NotImplementedError(
|
|
@@ -155,10 +248,117 @@ class IntentKitSkill(BaseTool):
|
|
|
155
248
|
raise ValueError("No AgentContext found")
|
|
156
249
|
return runtime.context
|
|
157
250
|
|
|
158
|
-
def
|
|
159
|
-
|
|
251
|
+
async def get_agent_skill_data(
|
|
252
|
+
self,
|
|
253
|
+
key: str,
|
|
254
|
+
) -> dict[str, Any] | None:
|
|
255
|
+
"""Retrieve persisted data for this skill scoped to the active agent."""
|
|
256
|
+
return await self.get_agent_skill_data_raw(self.name, key)
|
|
257
|
+
|
|
258
|
+
async def get_agent_skill_data_raw(
|
|
259
|
+
self,
|
|
260
|
+
skill_name: str,
|
|
261
|
+
key: str,
|
|
262
|
+
) -> dict[str, Any] | None:
|
|
263
|
+
"""Retrieve persisted data for a specific skill scoped to the active agent."""
|
|
264
|
+
context = self.get_context()
|
|
265
|
+
return await AgentSkillData.get(context.agent_id, skill_name, key)
|
|
266
|
+
|
|
267
|
+
async def save_agent_skill_data(self, key: str, data: dict[str, Any]) -> None:
|
|
268
|
+
"""Persist data for this skill scoped to the active agent."""
|
|
269
|
+
await self.save_agent_skill_data_raw(self.name, key, data)
|
|
270
|
+
|
|
271
|
+
async def save_agent_skill_data_raw(
|
|
272
|
+
self,
|
|
273
|
+
skill_name: str,
|
|
274
|
+
key: str,
|
|
275
|
+
data: dict[str, Any],
|
|
276
|
+
) -> None:
|
|
277
|
+
"""Persist data for a specific skill scoped to the active agent."""
|
|
160
278
|
context = self.get_context()
|
|
279
|
+
skill_data = AgentSkillDataCreate(
|
|
280
|
+
agent_id=context.agent_id,
|
|
281
|
+
skill=skill_name,
|
|
282
|
+
key=key,
|
|
283
|
+
data=data,
|
|
284
|
+
)
|
|
285
|
+
await skill_data.save()
|
|
286
|
+
|
|
287
|
+
async def delete_agent_skill_data(self, key: str) -> None:
|
|
288
|
+
"""Remove persisted data for this skill scoped to the active agent."""
|
|
289
|
+
context = self.get_context()
|
|
290
|
+
await AgentSkillData.delete(context.agent_id, self.name, key)
|
|
291
|
+
|
|
292
|
+
async def get_thread_skill_data(
|
|
293
|
+
self,
|
|
294
|
+
key: str,
|
|
295
|
+
) -> dict[str, Any] | None:
|
|
296
|
+
"""Retrieve persisted data for this skill scoped to the active chat."""
|
|
297
|
+
context = self.get_context()
|
|
298
|
+
return await ChatSkillData.get(context.chat_id, self.name, key)
|
|
299
|
+
|
|
300
|
+
async def save_thread_skill_data(self, key: str, data: dict[str, Any]) -> None:
|
|
301
|
+
"""Persist data for this skill scoped to the active chat."""
|
|
302
|
+
context = self.get_context()
|
|
303
|
+
skill_data = ChatSkillDataCreate(
|
|
304
|
+
chat_id=context.chat_id,
|
|
305
|
+
agent_id=context.agent_id,
|
|
306
|
+
skill=self.name,
|
|
307
|
+
key=key,
|
|
308
|
+
data=data,
|
|
309
|
+
)
|
|
310
|
+
await skill_data.save()
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
async def get_agentkit_actions(
|
|
314
|
+
agent_id: str,
|
|
315
|
+
provider_factories: Sequence[Callable[[], object]],
|
|
316
|
+
*,
|
|
317
|
+
agent: Agent | None = None,
|
|
318
|
+
) -> list[Action]:
|
|
319
|
+
"""Build an AgentKit instance and return its actions."""
|
|
320
|
+
|
|
321
|
+
if agent is None:
|
|
322
|
+
try:
|
|
323
|
+
context = IntentKitSkill.get_context()
|
|
324
|
+
except ValueError as exc: # pragma: no cover - defensive guard
|
|
325
|
+
raise IntentKitAPIError(
|
|
326
|
+
500,
|
|
327
|
+
"AgentContextMissing",
|
|
328
|
+
"Agent context is required to initialize AgentKit actions.",
|
|
329
|
+
) from exc
|
|
161
330
|
agent = context.agent
|
|
162
|
-
network_id = agent.network_id
|
|
163
331
|
|
|
164
|
-
|
|
332
|
+
if agent.id != agent_id:
|
|
333
|
+
raise IntentKitAPIError(
|
|
334
|
+
400,
|
|
335
|
+
"AgentMismatch",
|
|
336
|
+
"The requested agent does not match the active context agent.",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
wallet_provider: CdpEvmWalletProvider = await get_wallet_provider(agent)
|
|
340
|
+
|
|
341
|
+
agent_kit = AgentKit(
|
|
342
|
+
AgentKitConfig(
|
|
343
|
+
wallet_provider=wallet_provider,
|
|
344
|
+
action_providers=[factory() for factory in provider_factories],
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
return agent_kit.get_actions()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def action_to_structured_tool(action: Action) -> StructuredTool:
|
|
351
|
+
"""Convert an AgentKit action to a LangChain StructuredTool."""
|
|
352
|
+
|
|
353
|
+
def _tool_fn(**kwargs: object) -> str:
|
|
354
|
+
return action.invoke(kwargs)
|
|
355
|
+
|
|
356
|
+
tool = StructuredTool(
|
|
357
|
+
name=action.name,
|
|
358
|
+
description=action.description,
|
|
359
|
+
func=_tool_fn,
|
|
360
|
+
args_schema=action.args_schema,
|
|
361
|
+
)
|
|
362
|
+
tool.handle_tool_error = lambda e: f"tool error: {e}"
|
|
363
|
+
tool.handle_validation_error = lambda e: f"validation error: {e}"
|
|
364
|
+
return tool
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Basename AgentKit skills."""
|
|
2
|
+
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
from coinbase_agentkit import basename_action_provider
|
|
6
|
+
|
|
7
|
+
from intentkit.models.agent import Agent
|
|
8
|
+
from intentkit.skills.base import (
|
|
9
|
+
SkillConfig,
|
|
10
|
+
SkillState,
|
|
11
|
+
action_to_structured_tool,
|
|
12
|
+
get_agentkit_actions,
|
|
13
|
+
)
|
|
14
|
+
from intentkit.skills.basename.base import BasenameBaseTool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SkillStates(TypedDict):
|
|
18
|
+
BasenameActionProvider_register_basename: SkillState
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Config(SkillConfig):
|
|
22
|
+
"""Configuration for Basename skills."""
|
|
23
|
+
|
|
24
|
+
states: SkillStates
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def get_skills(
|
|
28
|
+
config: "Config",
|
|
29
|
+
is_private: bool,
|
|
30
|
+
agent_id: str,
|
|
31
|
+
agent: Agent | None = None,
|
|
32
|
+
**_,
|
|
33
|
+
) -> list[BasenameBaseTool]:
|
|
34
|
+
"""Get all Basename skills."""
|
|
35
|
+
|
|
36
|
+
available_skills: list[str] = []
|
|
37
|
+
for skill_name, state in config["states"].items():
|
|
38
|
+
if state == "disabled":
|
|
39
|
+
continue
|
|
40
|
+
if state == "public" or (state == "private" and is_private):
|
|
41
|
+
available_skills.append(skill_name)
|
|
42
|
+
|
|
43
|
+
actions = await get_agentkit_actions(
|
|
44
|
+
agent_id, [basename_action_provider], agent=agent
|
|
45
|
+
)
|
|
46
|
+
tools: list[BasenameBaseTool] = []
|
|
47
|
+
for skill in available_skills:
|
|
48
|
+
for action in actions:
|
|
49
|
+
if action.name.endswith(skill):
|
|
50
|
+
tools.append(action_to_structured_tool(action))
|
|
51
|
+
return tools
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" stop-color="#4c7dff" />
|
|
5
|
+
<stop offset="100%" stop-color="#0b2f80" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="128" height="128" fill="url(#bg)" rx="16" />
|
|
9
|
+
<text x="50%" y="52%" font-family="Arial,Helvetica,sans-serif" font-size="60" fill="#ffffff" font-weight="700" text-anchor="middle">B</text>
|
|
10
|
+
<text x="50%" y="86%" font-family="Arial,Helvetica,sans-serif" font-size="20" fill="#dbe4ff" text-anchor="middle">Base</text>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "Basename",
|
|
5
|
+
"description": "Basename ENS-style name registration via Coinbase AgentKit",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/basename/basename.svg",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"Crypto",
|
|
9
|
+
"Identity"
|
|
10
|
+
],
|
|
11
|
+
"properties": {
|
|
12
|
+
"enabled": {
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"title": "Enabled",
|
|
15
|
+
"description": "Whether this skill is enabled",
|
|
16
|
+
"default": false
|
|
17
|
+
},
|
|
18
|
+
"states": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"BasenameActionProvider_register_basename": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"title": "Register Basename",
|
|
24
|
+
"enum": [
|
|
25
|
+
"disabled",
|
|
26
|
+
"public",
|
|
27
|
+
"private"
|
|
28
|
+
],
|
|
29
|
+
"x-enum-title": [
|
|
30
|
+
"Disabled",
|
|
31
|
+
"Agent Owner + All Users",
|
|
32
|
+
"Agent Owner Only"
|
|
33
|
+
],
|
|
34
|
+
"description": "State for BasenameActionProvider_register_basename",
|
|
35
|
+
"default": "disabled"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"description": "States for each Basename skill (disabled, public, or private)"
|
|
39
|
+
},
|
|
40
|
+
"api_key_provider": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"title": "API Key Provider",
|
|
43
|
+
"description": "Who provides the API key",
|
|
44
|
+
"enum": [
|
|
45
|
+
"platform"
|
|
46
|
+
],
|
|
47
|
+
"x-enum-title": [
|
|
48
|
+
"Nation Hosted"
|
|
49
|
+
],
|
|
50
|
+
"default": "platform"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"required": [
|
|
54
|
+
"states",
|
|
55
|
+
"enabled"
|
|
56
|
+
],
|
|
57
|
+
"additionalProperties": true
|
|
58
|
+
}
|