{"version":3,"file":"molten.mjs","names":[],"sources":["../../../src/tools/molten.ts"],"sourcesContent":["/**\n * Molten Tool — Intent resolution layer for AI agents via api.molten.gg\n *\n * Three interaction modes:\n *   1. Conversations (guided) — describe what you need, Molten's concierge guides you\n *   2. Direct search — programmatic capability search with optional auto-execute\n *   3. Intents (async) — post offers/requests, ClawRank matches across the network\n *\n * Plus: matches, messaging, events, profile management, capability browsing.\n *\n * API docs: https://molten.gg/skill.md\n * Base URL: https://api.molten.gg/api/v1\n *\n * Requires MOLTEN_API_KEY for authenticated operations.\n * Use the `register` action first to get an API key.\n */\n\nimport { Type } from '@sinclair/typebox';\nimport { stringEnum, jsonResult, errorResult, readStringParam } from '../lib/tool-helpers.js';\nimport { getWalletState } from '../services/walletconnect-service.js';\nimport { checkToolConfig } from '../services/tool-config-service.js';\nimport { guardedFetch } from '../services/endpoint-allowlist.js';\nimport { getCredentialVault } from '../services/credential-vault.js';\n\n// ─── API Client ──────────────────────────────────────────────────────────\n\nconst MOLTEN_BASE_URL = 'https://api.molten.gg/api/v1';\n\nfunction getBaseUrl(): string {\n  return process.env.MOLTEN_BASE_URL || MOLTEN_BASE_URL;\n}\n\nfunction getMoltenApiKey(): string | undefined {\n  return getCredentialVault().getSecret('bot.molten.apiKey', 'molten') ?? _inMemoryApiKey ?? undefined;\n}\n\nlet _inMemoryApiKey: string | undefined;\n\nfunction setMoltenApiKey(apiKey: string): void {\n  // M10 FIX: Only store in memory, not process.env (reduces blast radius)\n  _inMemoryApiKey = apiKey;\n}\n\nasync function moltenFetch(\n  method: string,\n  path: string,\n  body?: unknown,\n  requireAuth = true,\n): Promise<any> {\n  const url = `${getBaseUrl()}${path}`;\n  const headers: Record<string, string> = {\n    'Content-Type': 'application/json',\n    'Accept': 'application/json',\n    'X-Client-Type': 'openclaw',\n  };\n\n  if (requireAuth) {\n    const apiKey = getMoltenApiKey();\n    if (!apiKey) throw new MoltenApiError(401, 'No API key set');\n    headers['Authorization'] = `Bearer ${apiKey}`;\n  }\n\n  const response = await guardedFetch(url, {\n    method,\n    headers,\n    ...(body ? { body: JSON.stringify(body) } : {}),\n    signal: AbortSignal.timeout(30000),\n  });\n\n  const data = await response.json().catch(() => ({})) as Record<string, any>;\n\n  if (!response.ok) {\n    const errMsg = data?.error?.message || data?.message || response.statusText;\n    const errCode = data?.error?.code || undefined;\n    throw new MoltenApiError(response.status, errMsg, errCode);\n  }\n\n  return data;\n}\n\nclass MoltenApiError extends Error {\n  status: number;\n  code?: string;\n  constructor(status: number, message: string, code?: string) {\n    super(message);\n    this.name = 'MoltenApiError';\n    this.status = status;\n    this.code = code;\n  }\n}\n\n// ─── Actions ─────────────────────────────────────────────────────────────\n\nconst ACTIONS = [\n  // Registration & profile\n  'register', 'profile', 'update_profile', 'claim_status',\n  // Conversations (guided flow)\n  'conversation', 'conversation_reply', 'conversation_status', 'list_conversations',\n  // Search (programmatic)\n  'search',\n  // Browse capabilities (no auth)\n  'browse', 'capability_details',\n  // Intents (async matching)\n  'create_intent', 'list_intents', 'cancel_intent',\n  // Matches\n  'list_matches', 'accept_match', 'reject_match', 'complete_match', 'match_message',\n  // Events\n  'check_events', 'ack_events',\n  // Webhooks\n  'setup_webhook',\n] as const;\n\nconst INTENT_TYPES = ['offer', 'request'] as const;\n\n// ─── Schema ──────────────────────────────────────────────────────────────\n\nconst MoltenSchema = Type.Object({\n  action: stringEnum(ACTIONS, {\n    description:\n      'register: register agent (returns API key). profile: get your profile. update_profile: update info. ' +\n      'conversation: start guided conversation. conversation_reply: reply to conversation. ' +\n      'search: direct capability search. browse: list all capabilities (no auth). ' +\n      'create_intent: post async offer/request. list_intents: your intents. cancel_intent: cancel. ' +\n      'list_matches: browse matches. accept_match/reject_match/complete_match: manage matches. ' +\n      'match_message: message a match. check_events: poll events. ack_events: acknowledge.',\n  }),\n\n  // Registration\n  name: Type.Optional(Type.String({ description: 'Agent name (lowercase, 2-64 chars)' })),\n  description: Type.Optional(Type.String({ description: 'Agent/intent description (up to 500 chars)' })),\n  client_type: Type.Optional(Type.String({ description: 'Client type: \"openclaw\", \"conway\", or \"generic\" (default: openclaw)' })),\n  wallet_address: Type.Optional(Type.String({ description: 'EVM wallet address (0x...)' })),\n  twitter_handle: Type.Optional(Type.String({ description: 'X/Twitter handle' })),\n\n  // Conversation\n  message: Type.Optional(Type.String({ description: 'Message for conversation/reply/match_message' })),\n  session_id: Type.Optional(Type.String({ description: 'Conversation session ID (for replies)' })),\n  confirm: Type.Optional(Type.Boolean({ description: 'Confirm execution in conversation' })),\n  cancel: Type.Optional(Type.Boolean({ description: 'Cancel current conversation flow' })),\n  selection: Type.Optional(Type.Number({ description: 'Select a match by index (1-based) in conversation' })),\n\n  // Search\n  query: Type.Optional(Type.String({ description: 'Natural language search query' })),\n  category: Type.Optional(Type.String({ description: 'Category filter for search/intents' })),\n  auto_execute: Type.Optional(Type.Boolean({ description: 'Auto-execute top search result (default: false)' })),\n\n  // Intents\n  intent_type: Type.Optional(stringEnum(INTENT_TYPES, { description: '\"offer\" or \"request\"' })),\n  attributes: Type.Optional(Type.String({ description: 'JSON attributes for intent' })),\n  expires_at: Type.Optional(Type.String({ description: 'Expiration timestamp (ISO 8601)' })),\n  min_match_score: Type.Optional(Type.Number({ description: 'Minimum ClawRank score for matches (0-100)' })),\n  auto_accept: Type.Optional(Type.Boolean({ description: 'Auto-accept matches above min score' })),\n\n  // IDs\n  intent_id: Type.Optional(Type.String({ description: 'Intent ID' })),\n  match_id: Type.Optional(Type.String({ description: 'Match ID' })),\n  plugin_id: Type.Optional(Type.String({ description: 'Capability/plugin ID for details' })),\n\n  // Events\n  event_ids: Type.Optional(Type.String({ description: 'Comma-separated event IDs to acknowledge' })),\n\n  // Webhooks\n  webhook_url: Type.Optional(Type.String({ description: 'Webhook URL for notifications' })),\n  webhook_events: Type.Optional(Type.String({ description: 'Comma-separated webhook event types' })),\n});\n\n// ─── Tool ────────────────────────────────────────────────────────────────\n\nexport function createMoltenTool() {\n  return {\n    name: 'molten',\n    label: 'Molten',\n    ownerOnly: true,\n    description:\n      'Intent resolution layer for AI agents on molten.gg. ' +\n      'Start conversations to find capabilities, search the network, post offers/requests, ' +\n      'and match with other agents. Use \"browse\" to see available capabilities (no auth needed). ' +\n      'Use \"conversation\" for guided discovery. Use \"search\" for direct programmatic lookups.',\n    parameters: MoltenSchema,\n    execute: async (_toolCallId: string, args: unknown) => {\n      const p = args as Record<string, unknown>;\n      const action = readStringParam(p, 'action', { required: true })!;\n\n      // browse and capability_details don't need auth\n      const noAuthActions = ['browse', 'capability_details', 'register'];\n      if (!noAuthActions.includes(action)) {\n        const notReady = checkToolConfig('molten');\n        if (notReady) return notReady;\n      }\n\n      try {\n        switch (action) {\n          // ── Registration & Profile ──────────────────────────────────\n          case 'register': {\n            const name = readStringParam(p, 'name', { required: true })!;\n            const description = readStringParam(p, 'description') || 'OpenClawnch DeFi agent';\n            const clientType = readStringParam(p, 'client_type') || 'openclaw';\n            const twitterHandle = readStringParam(p, 'twitter_handle');\n\n            // Auto-detect wallet from ClawnchConnect\n            let walletAddress = readStringParam(p, 'wallet_address');\n            if (!walletAddress) {\n              const walletState = getWalletState();\n              if (walletState.connected && walletState.address) {\n                walletAddress = walletState.address;\n              }\n            }\n            if (!walletAddress) {\n              return errorResult(\n                'wallet_address is required for registration. Connect a wallet first (/connect) ' +\n                'or provide wallet_address explicitly.'\n              );\n            }\n\n            const body: Record<string, unknown> = {\n              name,\n              client_type: clientType,\n              wallet_address: walletAddress,\n              description,\n            };\n            if (twitterHandle) body.twitter_handle = twitterHandle;\n\n            const result = await moltenFetch('POST', '/agents/register', body, false);\n\n            // Save the API key\n            if (result?.agent?.api_key) {\n              setMoltenApiKey(result.agent.api_key);\n            }\n\n            // H2 FIX: Never return API key in LLM-visible tool response\n            const maskedKey = result?.agent?.api_key\n              ? `${result.agent.api_key.slice(0, 8)}...${result.agent.api_key.slice(-4)}`\n              : undefined;\n            const { api_key: _stripped, ...safeResult } = result?.agent ?? {};\n            return jsonResult({\n              ...result,\n              agent: safeResult,\n              setup: maskedKey\n                ? `API key active (${maskedKey}). To persist, use /flykeys set MOLTEN_API_KEY with the full key shown during registration.`\n                : undefined,\n              claim: result?.agent?.claim_url\n                ? `Claim your agent: ${result.agent.claim_url}`\n                : undefined,\n            });\n          }\n\n          case 'profile': {\n            const result = await moltenFetch('GET', '/agents/me');\n            return jsonResult(result);\n          }\n\n          case 'update_profile': {\n            const body: Record<string, unknown> = {};\n            const description = readStringParam(p, 'description');\n            const twitterHandle = readStringParam(p, 'twitter_handle');\n            if (description) body.description = description;\n            if (twitterHandle) body.twitter_handle = twitterHandle;\n\n            const result = await moltenFetch('PATCH', '/agents/me', body);\n            return jsonResult(result);\n          }\n\n          case 'claim_status': {\n            const result = await moltenFetch('GET', '/agents/status');\n            return jsonResult(result);\n          }\n\n          // ── Conversations (Guided) ──────────────────────────────────\n          case 'conversation': {\n            const message = readStringParam(p, 'message', { required: true })!;\n            const result = await moltenFetch('POST', '/conversations', { message });\n            return jsonResult(result);\n          }\n\n          case 'conversation_reply': {\n            const sessionId = readStringParam(p, 'session_id', { required: true })!;\n            const message = readStringParam(p, 'message') || '';\n            const body: Record<string, unknown> = { message };\n            if (p.confirm === true) body.confirm = true;\n            if (p.cancel === true) body.cancel = true;\n            if (p.selection !== undefined) body.selection = p.selection;\n\n            const result = await moltenFetch('POST', `/conversations/${sessionId}/message`, body);\n            return jsonResult(result);\n          }\n\n          case 'conversation_status': {\n            const sessionId = readStringParam(p, 'session_id', { required: true })!;\n            const result = await moltenFetch('GET', `/conversations/${sessionId}`);\n            return jsonResult(result);\n          }\n\n          case 'list_conversations': {\n            const result = await moltenFetch('GET', '/conversations');\n            return jsonResult(result);\n          }\n\n          // ── Search (Programmatic) ───────────────────────────────────\n          case 'search': {\n            const query = readStringParam(p, 'query', { required: true })!;\n            const body: Record<string, unknown> = { query };\n            const category = readStringParam(p, 'category');\n            if (category) body.category = category;\n            if (p.auto_execute === true) body.autoExecute = true;\n\n            const result = await moltenFetch('POST', '/search', body);\n            return jsonResult(result);\n          }\n\n          // ── Browse Capabilities (No Auth) ───────────────────────────\n          case 'browse': {\n            const result = await moltenFetch('GET', '/plugins', undefined, false);\n            return jsonResult(result);\n          }\n\n          case 'capability_details': {\n            const pluginId = readStringParam(p, 'plugin_id', { required: true })!;\n            const result = await moltenFetch('GET', `/plugins/${pluginId}`, undefined, false);\n            return jsonResult(result);\n          }\n\n          // ── Intents (Async Matching) ────────────────────────────────\n          case 'create_intent': {\n            const intentType = readStringParam(p, 'intent_type', { required: true })!;\n            const description = readStringParam(p, 'description', { required: true })!;\n            const category = readStringParam(p, 'category');\n            const attributesStr = readStringParam(p, 'attributes');\n            const expiresAt = readStringParam(p, 'expires_at');\n            const minMatchScore = p.min_match_score as number | undefined;\n            const autoAccept = p.auto_accept as boolean | undefined;\n\n            let attributes: Record<string, unknown> | undefined;\n            if (attributesStr) {\n              try {\n                attributes = JSON.parse(attributesStr);\n              } catch {\n                return errorResult('attributes must be valid JSON');\n              }\n            }\n\n            const body: Record<string, unknown> = {\n              type: intentType,\n              description,\n            };\n            if (category) body.category = category;\n            if (attributes) body.attributes = attributes;\n            if (expiresAt) body.constraints = { expiresAt };\n            if (minMatchScore !== undefined || autoAccept !== undefined) {\n              body.matching = {\n                ...(minMatchScore !== undefined ? { minMatchScore } : {}),\n                ...(autoAccept !== undefined ? { autoAccept } : {}),\n              };\n            }\n\n            const result = await moltenFetch('POST', '/intents', body);\n            return jsonResult(result);\n          }\n\n          case 'list_intents': {\n            const result = await moltenFetch('GET', '/intents');\n            return jsonResult(result);\n          }\n\n          case 'cancel_intent': {\n            const intentId = readStringParam(p, 'intent_id', { required: true })!;\n            const result = await moltenFetch('DELETE', `/intents/${intentId}`);\n            return jsonResult({ status: 'cancelled', intentId, ...result });\n          }\n\n          // ── Matches ─────────────────────────────────────────────────\n          case 'list_matches': {\n            const result = await moltenFetch('GET', '/matches');\n            return jsonResult(result);\n          }\n\n          case 'accept_match': {\n            const matchId = readStringParam(p, 'match_id', { required: true })!;\n            const result = await moltenFetch('POST', `/matches/${matchId}/accept`);\n            return jsonResult(result);\n          }\n\n          case 'reject_match': {\n            const matchId = readStringParam(p, 'match_id', { required: true })!;\n            const result = await moltenFetch('POST', `/matches/${matchId}/reject`);\n            return jsonResult(result);\n          }\n\n          case 'complete_match': {\n            const matchId = readStringParam(p, 'match_id', { required: true })!;\n            const result = await moltenFetch('POST', `/matches/${matchId}/complete`);\n            return jsonResult(result);\n          }\n\n          case 'match_message': {\n            const matchId = readStringParam(p, 'match_id', { required: true })!;\n            const content = readStringParam(p, 'message', { required: true })!;\n            const result = await moltenFetch('POST', `/matches/${matchId}/message`, { content });\n            return jsonResult(result);\n          }\n\n          // ── Events ──────────────────────────────────────────────────\n          case 'check_events': {\n            const result = await moltenFetch('GET', '/events');\n            return jsonResult(result);\n          }\n\n          case 'ack_events': {\n            const eventIdsStr = readStringParam(p, 'event_ids', { required: true })!;\n            const event_ids = eventIdsStr.split(',').map(id => id.trim());\n            const result = await moltenFetch('POST', '/events/ack', { event_ids });\n            return jsonResult({ status: 'acknowledged', event_ids, ...result });\n          }\n\n          // ── Webhooks ────────────────────────────────────────────────\n          case 'setup_webhook': {\n            const webhookUrl = readStringParam(p, 'webhook_url', { required: true })!;\n            const eventsStr = readStringParam(p, 'webhook_events');\n            const events = eventsStr\n              ? eventsStr.split(',').map(e => e.trim())\n              : ['match.created', 'match.accepted', 'match.message'];\n\n            const result = await moltenFetch('POST', '/webhooks', { url: webhookUrl, events });\n            return jsonResult(result);\n          }\n\n          default:\n            return errorResult(`Unknown molten action: ${action}`);\n        }\n      } catch (err: any) {\n        if (err instanceof MoltenApiError) {\n          if (err.status === 401) {\n            return errorResult(\n              'Molten API key is invalid or expired.\\n\\nUpdate: `/flykeys set MOLTEN_API_KEY your_new_key`'\n            );\n          }\n          if (err.status === 403 && err.code === 'AGENT_NOT_CLAIMED') {\n            return errorResult(\n              'Your Molten agent hasn\\'t been claimed yet. Visit the claim URL from registration to activate it.'\n            );\n          }\n          if (err.status === 404) {\n            return errorResult(\n              `Molten returned 404: ${err.message}\\n\\n` +\n              'The resource may not exist, or the agent may need to be registered first.\\n' +\n              'Try \"register\" or \"browse\" to verify the API is reachable.'\n            );\n          }\n          if (err.status === 429) {\n            return errorResult('Molten rate limit exceeded. Wait a moment and try again.');\n          }\n          return errorResult(`Molten API error (${err.status}): ${err.message}`);\n        }\n        const msg = err instanceof Error ? err.message : String(err);\n        if (msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND') || msg.includes('fetch failed')) {\n          return errorResult(\n            'Could not reach api.molten.gg. The platform may be offline.\\n' +\n            'Check https://molten.gg for status.'\n          );\n        }\n        return errorResult(`Molten error: ${msg}`);\n      }\n    },\n  };\n}\n"],"mappings":"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"}