UNPKG

wowok_agent

Version:

Making It Easy for AI Agents to Communicate, Collaborate, Trade, and Trust.

748 lines (733 loc) β€’ 89.9 kB
#!/usr/bin/env node import { z } from "zod"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { readFileSync } from "fs"; import { resolve } from "path"; const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8")); import { CallService_DataSchema, CallMachine_DataSchema, MachineNode2File_InputSchema, MachineNode2File_OutputWrappedSchema, CallProgress_DataSchema, CallPermission_DataSchema, CallGuard_DataSchema, Guard2File_InputSchema, Guard2File_OutputWrappedSchema, CallArbitration_DataSchema, CallRepository_DataSchema, CallContact_DataSchema, CallTreasury_DataSchema, CallReward_DataSchema, CallAllocation_DataSchema, CallPersonal_DataSchema, CallPayment_DataSchema, CallDemand_DataSchema, CallOrder_DataSchema, CallEnvSchema, SubmissionCallSchema, strictParse, CallOutputSchema, handleCallResult, createServerConfig, createCapabilitiesConfig, createToolMeta, transformSubmission, getEnvConfig, WipGenerationOptionsSchema, WipToHtmlOptionsSchema, TokenDataFilterSchema, LocalInfoFilterSchema, LocalMarkFilterSchema, AccountFilterSchema, TokenTypeSchema, OnchainEventsInputSchema, OnchainEventsResultSchema, ProtocolInfoQuerySchema, ProtocolInfoResultSchema, TableAnswerSchema, TableItem_RepositoryDataSchema, TableItem_PermissionPermSchema, TableItem_EntityRegistrarSchema, TableItem_EntityLinkerSchema, TableItem_RewardRecordSchema, TableItem_DemandPresenterSchema, TableItem_TreasuryHistorySchema, TableItem_MachineNodeSchema, TableItem_ProgressHistorySchema, TableItem_AddressMarkSchema, NameOrAddressSchema, ObjectBaseSchema, AccountOrMark_AddressSchema, AccountOrMark_AddressAISchema, EntrypointSchema, AccountOperationOutputWrappedSchema, LocalMarkOperationOutputWrappedSchema, LocalInfoOperationOutputWrappedSchema, WipOperationOutputSchema, MessengerOperationOutputSchema, AccountOperationSchema, LocalMarkOperationSchema, LocalInfoOperationSchema, parseMachineNodesFromText, formatNodeErrors as formatMachineNodeErrors, MessengerOperationInputSchema, } from "./schema/index.js"; import { CallService, CallMachine, CallProgress, CallPermission, CallGuard, CallArbitration, CallRepository, CallContact, CallTreasury, CallReward, CallAllocation, CallPersonal, CallPayment, CallDemand, CallOrder, gen_passport, guard2file, parseGuardFile, formatGuardParseErrors, machineNode2file, generateNodeComments, generate_wip, verify_wip, sign_wip, wip2html, account_operation, local_mark_operation, local_info_operation, watch_conversations, send_message, send_file, watch_messages, extract_zip_messages, generate_wts, verify_wts, sign_wts, wts2html, proof_message, mark_messages_as_viewed, mark_conversation_as_viewed, query_local_mark_list, query_account_list, query_local_info_list, query_local_token_list, query_account, query_personal, query_objects, query_table, query_tableItem, queryProtocolInfo, query_received, queryTableItem_RepositoryData, queryTableItem_PermissionPerm, queryTableItem_RewardRecord, queryTableItem_DemandPresenter, queryTableItem_TreasuryHistory, queryTableItem_MachineNode, queryTableItem_ProgressHistory, queryTableItem_AddressMark, queryTableItem_EntityRegistrar, queryTableItem_EntityLinker, query_events, blacklist, friendslist, guardlist, settings, AmountType, } from "wowok"; const SERVER_DESCRIPTION = `WoWok MCP Server - Making It Easy for AI Agents to Communicate, Collaborate, Trade, and Trust. ## Token System Overview ### Default Platform Token: WOW - Token Type: 0x2::wow::WOW - Precision: 1_000_000_000 (9 decimal places) - This is the default token for all operations unless specified otherwise ### Multi-Token Support The platform supports ANY token (e.g., USDT, USDC, custom tokens). To use non-WOW tokens: 1. Use query_toolkit with query_type='token_list' to query available tokens and their precision info 2. Use the token's full type string (e.g., '0x2::usdt::USDT') in token_type parameter 3. Each token has its own precision - always verify before operations ### Token Precision Handling Different tokens have different precisions. ALWAYS query to verify: - WOW (default): 1_000_000_000 (9 decimals) - Other tokens: Use query_toolkit with query_type='token_list' to get actual precision - Never assume a token's decimal places - always query first ## CRITICAL: Token Amount Handling ### Amount Input Rules 1. WITH currency unit (e.g., '2WOW', '10.5WOW', '100USDT'): - Auto-converted using token's precision: 2WOW -> 2_000_000_000 - Use for: All on-chain token operations - MUST match the token_type parameter 2. WITHOUT currency unit (e.g., '2', '100'): - Used as-is: 2 -> 2 - Use for: Regular numeric parameters (counts, indices, etc.) ### Multi-Token Operation Examples - Create USDT-priced Service: token_type='0x2::usdt::USDT', amount='100USDT' - Allocate USDC rewards: token_type='0x2::usdc::USDC', amount='500USDC' - WOW remains default: omit token_type or use '0x2::wow::WOW' ### Security Warning Token amounts are HIGHLY SENSITIVE. Always: - Confirm user's intent before executing transfers - Verify token type and amount format (with/without unit) - Query token precision if unsure - Double-check large amount transactions - Never assume decimal places - use explicit token unit ## Tool Selection Guide | User Intent | Correct Tool | Wrong Tool (Avoid) | |------------|--------------|-------------------| | Create service listing | onchain_operations (service) | query_toolkit | | Send coins to address | onchain_operations (payment) | account_operation | | Check my balance | query_toolkit (account_balance) | onchain_operations | | Manage local wallet | account_operation | onchain_operations | | Export Guard for edit | guard2file | query_toolkit | | Send/Watch encrypted message | messenger_operation | onchain_operations | | Create workflow template | onchain_operations (machine) | wip_file | | Store my phone number | local_info_operation | onchain_operations | | Buy service (create order) | onchain_operations (order) | onchain_operations (service) | | Manage my orders | onchain_operations (order) | query_toolkit | | Apply for arbitration | onchain_operations (order) | onchain_operations (arbitration) | | Create reward pool for marketing | onchain_operations (reward) | onchain_operations (treasury) | | Claim rewards from pool | onchain_operations (reward) | query_toolkit | | Create fund allocation plan | onchain_operations (allocation) | onchain_operations (treasury) | | Execute fund distribution | onchain_operations (allocation) | onchain_operations (payment) | | Post service demand with reward | onchain_operations (demand) | onchain_operations (service) | | Submit solution for demand | onchain_operations (demand) | onchain_operations (order) | | Create team treasury | onchain_operations (treasury) | onchain_operations (allocation) | | Deposit/Withdraw from treasury | onchain_operations (treasury) | onchain_operations (payment) | | Create access control rules | onchain_operations (permission) | onchain_operations (guard) | | Create validation rules (Guard) | onchain_operations (guard) | onchain_operations (permission) | | Export Guard for editing | guard2file | query_toolkit | | Export Machine nodes for editing | machineNode2file | query_toolkit | | Query token list and precision | query_toolkit (token_list) | wowok_buildin_info | | Watch on-chain events | onchain_events | query_toolkit | | Generate WIP promise file | wip_file (generate) | messenger_operation | | Sign WIP file | wip_file (sign) | account_operation (signData) | | Verify WIP file integrity | wip_file (verify) | query_toolkit | | Send encrypted file via messenger | messenger_operation (send_file) | wip_file | | Generate witness timestamp (WTS) | messenger_operation (generate_wts) | wip_file | | Query protocol constants | wowok_buildin_info | query_toolkit | | Get documentation URL | documents_and_learn | wowok_buildin_info | ## Local vs On-chain Operations ### LOCAL ONLY (Never touch blockchain): - account_operation - local_mark_operation - local_info_operation ### ON-CHAIN (Blockchain transactions): - onchain_operations - messenger_operation (some operations) - wip_file (sign operation) ### QUERY (Read-only): - query_toolkit - onchain_events - wowok_buildin_info - documents_and_learn - guard2file - machineNode2file `; const server = new McpServer(createServerConfig(packageJson, SERVER_DESCRIPTION), createCapabilitiesConfig()); function convertAmountType(type) { if (typeof type === "number") return type; const upperType = type.toUpperCase(); if (upperType === "GUARDU64IDENTIFIER" || upperType === "GUARD_U64_IDENTIFIER") { return AmountType.GuardU64Identifier; } if (upperType === "FIXED") { return AmountType.Fixed; } return AmountType.Fixed; } function transformRewardData(data) { if (!data) return data; const transformed = { ...data }; if (transformed.guard_add && Array.isArray(transformed.guard_add)) { transformed.guard_add = transformed.guard_add.map((guard) => { if (guard.amount && typeof guard.amount.type === "string") { return { ...guard, amount: { ...guard.amount, type: convertAmountType(guard.amount.type) } }; } return guard; }); } return transformed; } const OnchainOperationsSchema = z.preprocess((input) => { if (typeof input === 'object' && input !== null) { const obj = { ...input }; if (typeof obj.description === 'string' && !obj.operation_type) { try { const parsed = JSON.parse(obj.description); if (parsed && typeof parsed === 'object' && parsed.operation_type) { return parsed; } } catch { } } if (typeof obj.data === 'string') { try { obj.data = JSON.parse(obj.data); } catch { } } if (typeof obj.env === 'string') { try { obj.env = JSON.parse(obj.env); } catch { } } if (typeof obj.submission === 'string') { try { obj.submission = JSON.parse(obj.submission); } catch { } } if (typeof obj.info === 'string') { try { obj.info = JSON.parse(obj.info); } catch { } } return obj; } return input; }, z.discriminatedUnion("operation_type", [ z.object({ operation_type: z.literal("service"), data: CallService_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸͺ Service Object: Create and manage product/service listings with transparent promises, bind workflow templates to order processing, set pricing, issue discount coupons to customers, and establish quality standards, etc.."), z.object({ operation_type: z.literal("machine"), data: CallMachine_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("βš™οΈ Machine Object: Design and deploy automated workflow templates (Machines) that define how services are delivered, etc.."), z.object({ operation_type: z.literal("progress"), data: CallProgress_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸ“Š Progress Object: Track and manage active workflows in real-time."), z.object({ operation_type: z.literal("repository"), data: CallRepository_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸ“¦ Repository Object: Read/write database with consensus field + address as key, strongly-typed data as value."), z.object({ operation_type: z.literal("arbitration"), data: CallArbitration_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("βš–οΈ Arbitration Object: Access a transparent on-chain arbitration system for resolving order conflicts."), z.object({ operation_type: z.literal("contact"), data: CallContact_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸ’¬ Contact Object: Manage on-chain instant messaging contact profiles."), z.object({ operation_type: z.literal("treasury"), data: CallTreasury_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸ’° Treasury Object: Create and manage treasury for team funds with deposit/withdrawal rules, etc.."), z.object({ operation_type: z.literal("reward"), data: CallReward_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("🎁 Reward Object: Create reward pools and set claim conditions by Guard verification."), z.object({ operation_type: z.literal("allocation"), data: CallAllocation_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸ“€ Allocation Object: Create distribution plans to auto-distribute funds to multiple recipients."), z.object({ operation_type: z.literal("permission"), data: CallPermission_DataSchema, env: CallEnvSchema.optional(), }).describe("πŸ” Permission Object: Define who can perform which operations on WoWok objects. Important Note: If needed, you should first query 'guard instructions' through the 'wowok_buildin_info' tool."), z.object({ operation_type: z.literal("guard"), data: CallGuard_DataSchema, env: CallEnvSchema.optional(), }).describe("πŸ›‘οΈ Guard Object: Create immutable programmable validation rules that return boolean results. Set 'namedNew' to name the new Guard. Use root.type='node' for direct node tree or root.type='file' to load from file. Use 'wowok_buildin_info' tool with query='guard instructions' for all available operations. NOTE for EntityLinker/EntityRegistrar queries: Add system addresses to the Guard table - ENTITY_LINKER_ADDRESS (0xaaa) for EntityLinker, ENTITY_REGISTRAR_ADDRESS (0xaab) for EntityRegistrar."), z.object({ operation_type: z.literal("personal"), data: CallPersonal_DataSchema, env: CallEnvSchema.optional(), }).describe("πŸ†” Public Identity Profile: Establish and manage your on-chain public identity. ⚠️ CRITICAL: Everything here is PERMANENTLY PUBLIC on the blockchain!"), z.object({ operation_type: z.literal("payment"), data: CallPayment_DataSchema, env: CallEnvSchema.optional(), }).describe("πŸ’° Payment Object: Send instant, irreversible coin transfers to any wallet address."), z.object({ operation_type: z.literal("demand"), data: CallDemand_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("🎯 Demand Object: Post service requests with reward pools on-chain."), z.object({ operation_type: z.literal("order"), data: CallOrder_DataSchema, env: CallEnvSchema.optional(), submission: SubmissionCallSchema.optional(), }).describe("πŸ“¦ Order Object: Manage the order lifecycle, Including operating order arbitration (Arb Object associated with the order), advancing progress (Progress Object associated with the order), extracting order refunds/payments, setting agents, etc."), z.object({ operation_type: z.literal("gen_passport"), guard: z.union([z.string(), z.array(z.string())]).describe("Guard object ID(s) to verify and generate passport from. Can be a single guard (string) or multiple guards (array of strings). Supports guard names or addresses."), info: SubmissionCallSchema.optional().describe("Optional submission data. If not provided, will attempt to get existing submissions from the guard."), env: CallEnvSchema.optional(), }).describe("πŸ›‚ Generate Verified Passport Object: Create immutable verified credentials after Guard validation passes. Supports verifying multiple guards at once."), ])); const WipOperationsSchema = z.preprocess((input) => { if (typeof input === 'object' && input !== null) { const obj = { ...input }; if (typeof obj.description === 'string' && !obj.type) { try { const parsed = JSON.parse(obj.description); if (parsed && typeof parsed === 'object' && parsed.type) { return parsed; } } catch { } } if (typeof obj.options === 'string') { try { obj.options = JSON.parse(obj.options); } catch { } } return obj; } return input; }, z.discriminatedUnion("type", [ z.object({ type: z.literal("generate"), options: WipGenerationOptionsSchema.describe("WIP generation options"), outputPath: z.string().describe("Output file path (.wip file). If file exists, it will be overwritten"), }).describe("Generate WIP file from markdown text and optional images"), z.object({ type: z.literal("verify"), wipFilePath: z.string().describe("WIP file path to verify. Supports: 1) Local file path (e.g., '/path/to/file.wip', 'C:\\Users\\name\\doc.wip'), 2) Network URL (e.g., 'https://example.com/doc.wip', 'http://site.com/file.wip'), 3) Data URL (e.g., 'data:application/json;base64,eyJ3aXAiOi...')"), hash_equal: z.string().optional().describe("Optional expected hash value. If provided, the function will first verify if the file's hash matches this value. If not matched, returns hash mismatch error."), requireSignature: z.boolean().optional().describe("Optional flag to require digital signature. If true, verification will fail if WIP file has no signature"), }).describe("Verify WIP file integrity and signatures"), z.object({ type: z.literal("sign"), wipFilePath: z.string().describe("WIP file path to sign. Supports: 1) Local file path (e.g., '/path/to/file.wip'), 2) Network URL (e.g., 'https://example.com/doc.wip'). The file will be loaded, validated, and signed"), account: z.string().optional().describe("Signing account (account name or address). If not specified, uses default account"), outputPath: z.string().optional().describe("Output file path. If not specified, adds 'signed_' prefix to original file name (e.g., 'doc.wip' becomes 'signed_doc.wip')"), }).describe("Sign WIP file with account"), z.object({ type: z.literal("wip2html"), wipPath: z.string().describe("WIP file path or directory path. Supports: 1) Single WIP file (e.g., '/path/to/file.wip'), 2) Directory containing .wip files (e.g., '/path/to/wips/'), 3) Network URL (e.g., 'https://example.com/doc.wip'). When directory is provided, all .wip files in the directory will be converted to HTML"), options: WipToHtmlOptionsSchema.optional().describe("Conversion options"), }).describe("Convert WIP file to HTML format"), ])); const OnchainTableDataSchema = z.preprocess((input) => { if (typeof input === 'object' && input !== null && !Array.isArray(input)) { const obj = { ...input }; if (typeof obj.description === 'string' && !obj.query_type) { try { const parsed = JSON.parse(obj.description); if (parsed && typeof parsed === 'object' && parsed.query_type) { return parsed; } } catch { } } if (obj.cursor === '') obj.cursor = null; if (obj.limit === '') obj.limit = null; return obj; } return input; }, z.union([ z.discriminatedUnion("query_type", [ z.object({ query_type: z.literal("onchain_table"), parent: NameOrAddressSchema.describe("Parent object ID whose dynamic fields table to query"), cursor: z.union([z.string(), z.null()]).optional().describe("Pagination cursor from previous page's nextCursor"), limit: z.union([z.number(), z.null()]).optional().describe("Max items per page"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Paginated query of an on-chain object's dynamic fields table β€” returns items with keys, types, and object IDs. Use to explore all entries (e.g., all orders in a Service, all records in a Repository). Returns: TableAnswer | undefined (items[], nextCursor, hasNextPage)"), z.object({ query_type: z.literal("onchain_table_item_repository_data"), parent: NameOrAddressSchema.describe("Parent Repository object ID"), name: z.string().describe("Name/key of the record to retrieve from the repository"), entity: z.union([AccountOrMark_AddressSchema, z.number()]).describe("Entity ID or address that owns/identifies the record"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a specific record from a Repository's on-chain key-value database by name and entity. Use to read stored data records. Returns: TableItem_RepositoryData | undefined"), z.object({ query_type: z.literal("onchain_table_item_permission_perm"), parent: NameOrAddressSchema.describe("Parent Permission object ID"), address: z.union([AccountOrMark_AddressSchema, z.string()]).describe("User address or Guard ID whose permissions to check"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a permission entry from a Permission object's perm table β€” checks what operations a user/guard is allowed to perform. Use to verify access rights. Returns: TableItem_PermissionPerm | undefined"), z.object({ query_type: z.literal("onchain_table_item_entity_registrar"), address: z.union([AccountOrMark_AddressSchema, z.string()]).describe("User address to look up in the global EntityRegistrar"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query an entity's registration record from the global EntityRegistrar β€” verifies if an address is registered on the blockchain. Use to check registration status and details. Returns: TableItem_EntityRegistrar | undefined"), z.object({ query_type: z.literal("onchain_table_item_entity_linker"), address: z.union([AccountOrMark_AddressSchema, z.string()]).describe("Entity address whose community votes/endorsements to query"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query community votes/endorsements for an entity from the global EntityLinker β€” shows how others have voted on this entity. Use to check reputation and community trust. Returns: TableItem_EntityLinker | undefined"), z.object({ query_type: z.literal("onchain_table_item_reward_record"), parent: NameOrAddressSchema.describe("Parent Reward object ID"), address: AccountOrMark_AddressSchema.describe("User address that claimed the reward"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a reward claim record from a Reward object's table β€” checks if a user has claimed a specific incentive. Use to verify reward distribution status. Returns: TableItem_RewardRecord | undefined"), ]), z.discriminatedUnion("query_type", [ z.object({ query_type: z.literal("onchain_table_item_demand_presenter"), parent: NameOrAddressSchema.describe("Parent Demand object ID"), address: AccountOrMark_AddressSchema.describe("Presenter address that submitted the demand"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a demand submission from a Demand object's table β€” Demands are service requests submitted by users. Use to check a specific demand's details. Returns: TableItem_DemandPresenter | undefined"), z.object({ query_type: z.literal("onchain_table_item_treasury_history"), parent: NameOrAddressSchema.describe("Parent Treasury object ID"), address: AccountOrMark_AddressSchema.describe("Payment ID whose treasury record to look up"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a payment record from a Treasury's history table by payment ID β€” Treasury manages funds and payment tracking. Use to look up payment details and status. Returns: TableItem_TreasuryHistory | undefined"), z.object({ query_type: z.literal("onchain_table_item_machine_node"), parent: NameOrAddressSchema.describe("Parent Machine object ID"), key: z.string().describe("Node name (string key) to query in the Machine's workflow definition"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a workflow node definition from a Machine object's table by node name β€” Machines define workflow templates. Use to inspect a specific node's configuration and logic. Returns: TableItem_MachineNode | undefined"), z.object({ query_type: z.literal("onchain_table_item_progress_history"), parent: NameOrAddressSchema.describe("Parent Progress object ID"), u64: z.union([z.number(), z.string()]).describe("Sequence number (u64) of the progress step to query"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a progress step record from a Progress object's table by sequence number β€” Progress tracks order/workflow execution. Use to check the status of a specific execution step. Returns: TableItem_ProgressHistory | undefined"), z.object({ query_type: z.literal("onchain_table_item_address_mark"), parent: NameOrAddressSchema.describe("Parent AddressMark object ID"), address: AccountOrMark_AddressSchema.describe("Address whose PUBLIC on-chain name/tags to look up"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a PUBLIC on-chain name/tag mark for an address β€” unlike local marks, these are published on-chain. Use to look up public labels attached to any address. Returns: TableItem_AddressMark | undefined"), z.object({ query_type: z.literal("onchain_table_item_generic"), parent: NameOrAddressSchema.describe("Parent object ID whose dynamic field to query"), key_type: z.string().describe("Type of the key (e.g., 'address', 'u64', 'string', '0x2::object::ID')"), key_value: z.any().describe("Value of the key. Must match the key_type format"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query a generic table item from ANY object's dynamic fields β€” supports arbitrary key types and values for non-WoWok objects. Use for custom objects and general-purpose table lookups. Returns: ObjectBase | undefined"), ]), ])); const OnchainTableDataResultSchema = z.object({ result: z.union([ z.object({ query_type: z.literal("onchain_table"), result: z.union([TableAnswerSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_repository_data"), result: z.union([TableItem_RepositoryDataSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_permission_perm"), result: z.union([TableItem_PermissionPermSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_entity_registrar"), result: z.union([TableItem_EntityRegistrarSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_entity_linker"), result: z.union([TableItem_EntityLinkerSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_reward_record"), result: z.union([TableItem_RewardRecordSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_demand_presenter"), result: z.union([TableItem_DemandPresenterSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_treasury_history"), result: z.union([TableItem_TreasuryHistorySchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_machine_node"), result: z.union([TableItem_MachineNodeSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_progress_history"), result: z.union([TableItem_ProgressHistorySchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_address_mark"), result: z.union([TableItem_AddressMarkSchema, z.undefined()]) }), z.object({ query_type: z.literal("onchain_table_item_generic"), result: z.union([ObjectBaseSchema, z.undefined()]) }), ]) }); async function handleOnchainTableData(args) { const validated = strictParse(OnchainTableDataSchema, args, "onchain_table_data input"); let result; switch (validated.query_type) { case "onchain_table": { const queryResult = await query_table({ parent: validated.parent, cursor: validated.cursor, limit: validated.limit, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table", result: queryResult }; break; } case "onchain_table_item_repository_data": { const queryResult = await queryTableItem_RepositoryData({ parent: validated.parent, name: validated.name, entity: validated.entity, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_repository_data", result: queryResult }; break; } case "onchain_table_item_permission_perm": { const queryResult = await queryTableItem_PermissionPerm({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_permission_perm", result: queryResult }; break; } case "onchain_table_item_reward_record": { const queryResult = await queryTableItem_RewardRecord({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_reward_record", result: queryResult }; break; } case "onchain_table_item_demand_presenter": { const queryResult = await queryTableItem_DemandPresenter({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_demand_presenter", result: queryResult }; break; } case "onchain_table_item_treasury_history": { const queryResult = await queryTableItem_TreasuryHistory({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_treasury_history", result: queryResult }; break; } case "onchain_table_item_machine_node": { const queryResult = await queryTableItem_MachineNode({ parent: validated.parent, key: validated.key, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_machine_node", result: queryResult }; break; } case "onchain_table_item_progress_history": { const queryResult = await queryTableItem_ProgressHistory({ parent: validated.parent, u64: validated.u64, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_progress_history", result: queryResult }; break; } case "onchain_table_item_address_mark": { const queryResult = await queryTableItem_AddressMark({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_address_mark", result: queryResult }; break; } case "onchain_table_item_entity_registrar": { const queryResult = await queryTableItem_EntityRegistrar({ address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_entity_registrar", result: queryResult }; break; } case "onchain_table_item_entity_linker": { const queryResult = await queryTableItem_EntityLinker({ address: validated.address, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_entity_linker", result: queryResult }; break; } case "onchain_table_item_generic": { const queryResult = await query_tableItem({ parent: validated.parent, key: { type: validated.key_type, value: validated.key_value }, no_cache: validated.no_cache, network: validated.network }); result = { query_type: "onchain_table_item_generic", result: queryResult }; break; } default: throw new Error(`Unknown table query type. Valid query_types are: "onchain_table", "onchain_table_item_repository_data", "onchain_table_item_permission_perm", "onchain_table_item_reward_record", "onchain_table_item_demand_presenter", "onchain_table_item_treasury_history", "onchain_table_item_machine_node", "onchain_table_item_progress_history", "onchain_table_item_address_mark", "onchain_table_item_entity_registrar", "onchain_table_item_entity_linker", "onchain_table_item_generic"`); } return { content: [{ type: "text", text: JSON.stringify({ result }) }], structuredContent: { result }, }; } const WatchQueryOperationsSchema = z.preprocess((input) => { if (typeof input === 'object' && input !== null) { const obj = { ...input }; if (typeof obj.description === 'string' && !obj.query_type) { try { const parsed = JSON.parse(obj.description); if (parsed && typeof parsed === 'object' && parsed.query_type) { return parsed; } } catch { } } for (const key of ['filter', 'objects', 'env', 'token_type']) { if (typeof obj[key] === 'string') { try { obj[key] = JSON.parse(obj[key]); } catch { } } } return obj; } return input; }, z.union([ z.discriminatedUnion("query_type", [ z.object({ query_type: z.literal("local_mark_list"), filter: LocalMarkFilterSchema.optional().describe("Local mark filter"), }).describe("Query your LOCAL address book β€” maps human-readable names to blockchain addresses with optional tags. Use to resolve namesβ†’addresses or find addresses by tag. Returns: MarkData[] (name, address, tags, timestamps)"), z.object({ query_type: z.literal("account_list"), filter: AccountFilterSchema.optional().describe("Account filter"), }).describe("Query your LOCAL accounts β€” view all accounts stored on this device (addresses, public keys, messenger status, suspension state). Use to discover available accounts before operations. Returns: AccountData[] (name, address, pubkey, suspended, messenger, timestamps)"), z.object({ query_type: z.literal("local_info_list"), filter: LocalInfoFilterSchema.optional().describe("Local info filter"), }).describe("Query your LOCAL private info β€” sensitive data like delivery addresses, phone numbers, contacts stored ONLY on this device. Use to retrieve saved contact/delivery details. Returns: InfoData[] (name, default value, contents, timestamps)"), z.object({ query_type: z.literal("token_list"), filter: TokenDataFilterSchema.optional(), }).describe("Query cached token metadata β€” symbol, decimals, icon URL, description for tokens previously fetched from chain. Use to look up token info without an on-chain query. Returns: TokenTypeInfo[] (type, alias, name, symbol, decimals, iconUrl)"), z.object({ query_type: z.literal("account_balance"), name_or_address: NameOrAddressSchema.optional().describe("Account name or address. Use empty string '' for the default account. Defaults to '' if omitted."), balance: z.boolean().optional().describe("Set to true to query total balance amount for the token type"), coin: z.object({ cursor: z.union([z.string(), z.null()]).optional().describe("Pagination cursor for listing coin objects"), limit: z.union([z.number(), z.null()]).optional().describe("Max coin objects per page"), }).optional().describe("Set to query paginated coin objects instead of balance. Use cursor/limit for pagination."), token_type: TokenTypeSchema.optional().describe("Token type to query; defaults to 0x2::wow::WOW (platform token)"), network: EntrypointSchema.optional(), }).describe("Query an account's coin balance OR paginated coin objects. Use balance=true for total amount, or coin={cursor,limit} to list individual coin objects. Returns: { address, balance? | coin? }"), ]), z.discriminatedUnion("query_type", [ z.object({ query_type: z.literal("onchain_personal_profile"), account: NameOrAddressSchema.optional().describe("Account name or ID to query. Use empty string '' for the default account."), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query any user's PUBLIC on-chain profile β€” social links, reputation (likes/dislikes), personal info records, voting history, referrer. Use to look up a user's public identity and reputation. Returns: ObjectPersonal | undefined"), z.object({ query_type: z.literal("onchain_objects"), objects: z.array(NameOrAddressSchema).describe("List of object IDs (names or addresses) to query in batch"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Batch query on-chain WOWOK objects by ID or name β€” supports Service, Machine, Order, Treasury, Reward, Arb, Personal, Contact, and more. Use to inspect one or more objects in a single call. Returns: { objects: ObjectBase[] }"), z.object({ query_type: z.literal("onchain_received"), name_or_address: AccountOrMark_AddressAISchema.describe("Name or address of the object that received payments/items - can be a string (name/address) or full object"), all_type: z.boolean().optional().describe("Set to true to query all token types received; defaults to the object's Token type '0x2::payment::CoinWrapper<TOKEN>' (Coins wrapper sent via Payment object). If the object has no Token type, the call will fail."), cursor: z.union([z.string(), z.null()]).optional().describe("Pagination cursor from previous page"), limit: z.union([z.number(), z.null()]).optional().describe("Max records per page"), no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"), network: EntrypointSchema.optional(), }).describe("Query objects (Payments, Tokens, NFTs) received by a specific object. Use to track incoming payments or items sent to an on-chain object. Supports pagination and all_type filter. Returns: ReceivedBalance | ReceivedNormal[]"), ]), ])); async function handleOnchainOperations(args) { try { if (typeof args === 'object' && args !== null && args.description && !args.operation_type) { const hasWrappedParams = typeof args.description === 'string' && (args.description.includes('operation_type') || args.description.includes('data')); if (hasWrappedParams) { throw new Error("Invalid parameter structure. Parameters should NOT be wrapped in 'description' field.\n" + "Correct format: { operation_type: '...', data: {...} }\n" + "Incorrect format: { description: '{ operation_type: ... }' }"); } } const validated = strictParse(OnchainOperationsSchema, args, "onchain_operations input"); const env = getEnvConfig(validated.env); switch (validated.operation_type) { case "service": { const callService = new CallService(validated.data); const result = validated.submission ? await callService.call_with_submission(env, await transformSubmission(validated.submission)) : await callService.call(env); return handleCallResult(result); } case "machine": { if (validated.data.node && typeof validated.data.node === 'object' && 'json_or_markdown_file' in validated.data.node) { const filePathStr = validated.data.node.json_or_markdown_file; let fileContent; try { const filePath = resolve(filePathStr); fileContent = readFileSync(filePath, 'utf-8'); } catch (e) { throw new Error(`Failed to read file '${filePathStr}': ${e.message}`); } const parseResult = parseMachineNodesFromText(fileContent); if (!parseResult.success) { const errorMessages = formatMachineNodeErrors(parseResult.errors); throw new Error(`Machine node validation failed for file '${filePathStr}':\n\n${errorMessages}`); } if (!parseResult.data) { throw new Error(`No nodes found in file '${filePathStr}'`); } validated.data.node = { op: "set", nodes: parseResult.data, bReplace: true }; } const callMachine = new CallMachine(validated.data); const result = validated.submission ? await callMachine.call_with_submission(env, await transformSubmission(validated.submission)) : await callMachine.call(env); return handleCallResult(result); } case "progress": { const callProgress = new CallProgress(validated.data); const result = validated.submission ? await callProgress.call_with_submission(env, await transformSubmission(validated.submission)) : await callProgress.call(env); return handleCallResult(result); } case "repository": { const callRepository = new CallRepository(validated.data); const result = validated.submission ? await callRepository.call_with_submission(env, await transformSubmission(validated.submission)) : await callRepository.call(env); return handleCallResult(result); } case "arbitration": { const callArbitration = new CallArbitration(validated.data); const result = validated.submission ? await callArbitration.call_with_submission(env, await transformSubmission(validated.submission)) : await callArbitration.call(env); return handleCallResult(result); } case "contact": { const callContact = new CallContact(validated.data); const result = validated.submission ? await callContact.call_with_submission(env, await transformSubmission(validated.submission)) : await callContact.call(env); return handleCallResult(result); } case "treasury": { const callTreasury = new CallTreasury(validated.data); const result = validated.submission ? await callTreasury.call_with_submission(env, await transformSubmission(validated.submission)) : await callTreasury.call(env); return handleCallResult(result); } case "reward": { const transformedData = transformRewardData(validated.data); const callReward = new CallReward(transformedData); const result = validated.submission ? await callReward.call_with_submission(env, await transformSubmission(validated.submission)) : await callReward.call(env); return handleCallResult(result); } case "allocation": { const callAllocation = new CallAllocation(validated.data); const result = validated.submission ? await callAllocation.call_with_submission(env, await transformSubmission(validated.submission)) : await callAllocation.call(env); return handleCallResult(result); } case "permission": { const callPermission = new CallPermission(validated.data); const result = await callPermission.call(env); return handleCallResult(result); } case "guard": { const validatedData = validated.data; if (validatedData.root?.type === "file") { const filePath = resolve(validatedData.root.file_path); let fileContent; try { fileContent = readFileSync(filePath, 'utf-8'); } catch (e) { throw new Error(`Failed to read Guard definition file '${validatedData.root.file_path}': ${e.message}`); } const parseResult = parseGuardFile(fileContent, validatedData.root.format); if (!parseResult.success) { const errorMessages = formatGuardParseErrors(parseResult.errors); throw new Error(`Guard validation failed for file '${validatedData.root.file_path}':\n\n${errorMessages}`); } const fileData = parseResult.data; validatedData.root = { type: "node", node: fileData.root }; if (fileData.table !== undefined && validatedData.table === undefined) { validatedData.table = fileData.table; } if (fileData.description !== undefined && validatedData.description === undefined) { validatedData.description = fileData.description; } if (fileData.rely !== undefined && validatedData.rely === undefined) { validatedData.rely = fileData.rely; } if (fileData.namedNew !== undefined && validatedData.namedNew === undefined) { validatedData.namedNew = fileData.namedNew; } } if (validatedData.root && typeof validatedData.root === 'object') { const rootObj = validatedData.root; if (rootObj.type === 'node' && rootObj.node) { validatedData.root = rootObj.node; } } if (!validatedData.table || validatedData