{"version":3,"file":"molten-command.mjs","names":[],"sources":["../../../src/commands/molten-command.ts"],"sourcesContent":["/**\n * /molten command — quick Molten status check.\n *\n * Shows agent profile, claim status, and conversation count.\n * If not configured, shows setup instructions.\n *\n * Uses the real Molten API at api.molten.gg/api/v1.\n */\n\nimport { getCredentialVault } from '../services/credential-vault.js';\nimport { guardedFetch } from '../services/endpoint-allowlist.js';\n\nconst MOLTEN_BASE_URL = 'https://api.molten.gg/api/v1';\n\nexport const moltenCommand = {\n  name: 'molten',\n  description: 'Show your Molten agent status',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async (_ctx: any) => {\n    const apiKey = getCredentialVault().getSecret('bot.molten.apiKey', 'molten-command');\n\n    if (!apiKey) {\n      return {\n        text:\n          '**Molten is not configured.**\\n\\n' +\n          'Molten is an intent resolution layer for AI agents. ' +\n          'Express what you need, Molten finds the best way to fulfill it.\\n\\n' +\n          'Ask me to \"register on Molten\" to get started, or set your key:\\n' +\n          '  `/flykeys set MOLTEN_API_KEY your_key`',\n      };\n    }\n\n    // H8: Removed MOLTEN_BASE_URL env override — prevents credential exfiltration\n    // via attacker-controlled URL. Always use the hardcoded production URL.\n    const baseUrl = MOLTEN_BASE_URL;\n\n    try {\n      // Try /agents/me for profile\n      const profileRes = await guardedFetch(`${baseUrl}/agents/me`, {\n        headers: {\n          'Authorization': `Bearer ${apiKey}`,\n          'Accept': 'application/json',\n          'X-Client-Type': 'openclawnch',\n        },\n        signal: AbortSignal.timeout(15000),\n      });\n\n      if (!profileRes.ok) {\n        const data = await profileRes.json().catch(() => ({})) as Record<string, any>;\n        const errCode = data?.error?.code;\n\n        if (profileRes.status === 401) {\n          return {\n            text: '**Molten API key is invalid or expired.**\\n\\n' +\n              'Update: `/flykeys set MOLTEN_API_KEY your_new_key`',\n          };\n        }\n\n        if (profileRes.status === 403 && errCode === 'AGENT_NOT_CLAIMED') {\n          return {\n            text: '**Molten agent not claimed yet.**\\n\\n' +\n              'Your agent is registered but needs to be claimed.\\n' +\n              'Visit the claim URL you received during registration to activate.',\n          };\n        }\n\n        if (profileRes.status === 404) {\n          return {\n            text: '**Molten agent not found.**\\n\\n' +\n              'Your API key doesn\\'t match a registered agent.\\n' +\n              'Ask me to \"register on Molten\" to create a new agent.',\n          };\n        }\n\n        return {\n          text: `Molten error (${profileRes.status}): ${data?.error?.message || profileRes.statusText}`,\n        };\n      }\n\n      const profile = await profileRes.json() as Record<string, any>;\n      const agent = profile?.agent || profile;\n\n      const lines = [\n        '**Molten Agent**',\n        '',\n        `Name: ${agent.name || 'unknown'}`,\n        `ID: ${agent.id || 'unknown'}`,\n      ];\n\n      if (agent.description) lines.push(`Description: ${agent.description}`);\n      if (agent.client_type) lines.push(`Type: ${agent.client_type}`);\n      if (agent.wallet_address) lines.push(`Wallet: ${agent.wallet_address.slice(0, 6)}...${agent.wallet_address.slice(-4)}`);\n      if (agent.status) lines.push(`Status: ${agent.status}`);\n      if (agent.claw_rank_score !== undefined) lines.push(`ClawRank: ${agent.claw_rank_score}/100`);\n\n      // Try to get conversation count\n      try {\n        const convRes = await guardedFetch(`${baseUrl}/conversations`, {\n          headers: {\n            'Authorization': `Bearer ${apiKey}`,\n            'Accept': 'application/json',\n          },\n          signal: AbortSignal.timeout(10000),\n        });\n        if (convRes.ok) {\n           const convData = await convRes.json() as Record<string, any>;\n           const count = convData?.conversations?.length ?? convData?.count ?? 0;\n          if (count > 0) {\n            lines.push('');\n            lines.push(`Active conversations: ${count}`);\n          }\n        }\n      } catch {\n        // Skip silently\n      }\n\n      // Try to get event count\n      try {\n        const eventsRes = await guardedFetch(`${baseUrl}/events`, {\n          headers: {\n            'Authorization': `Bearer ${apiKey}`,\n            'Accept': 'application/json',\n          },\n          signal: AbortSignal.timeout(10000),\n        });\n        if (eventsRes.ok) {\n           const eventsData = await eventsRes.json() as Record<string, any>;\n           const events = eventsData?.events || [];\n          if (events.length > 0) {\n            lines.push(`Unread events: ${events.length}`);\n          }\n        }\n      } catch {\n        // Skip silently\n      }\n\n      lines.push('');\n      lines.push('Use \"search on Molten for...\" or \"start a Molten conversation about...\" to interact.');\n\n      return { text: lines.join('\\n') };\n    } catch (err: any) {\n      const msg = err instanceof Error ? err.message : String(err);\n      if (msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND') || msg.includes('fetch failed') || msg.includes('abort')) {\n        return {\n          text: '**Molten API unreachable.**\\n\\n' +\n            'Could not connect to api.molten.gg.\\n' +\n            'Check https://molten.gg for status.',\n        };\n      }\n      return { text: `Molten error: ${msg}` };\n    }\n  },\n};\n"],"mappings":";;;;;;;;;;;AAYA,MAAM,kBAAkB;AAExB,MAAa,gBAAgB;CAC3B,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,OAAO,SAAc;EAC5B,MAAM,SAAS,oBAAoB,CAAC,UAAU,qBAAqB,iBAAiB;AAEpF,MAAI,CAAC,OACH,QAAO,EACL,MACE,uQAKH;EAKH,MAAM,UAAU;AAEhB,MAAI;GAEF,MAAM,aAAa,MAAM,aAAa,GAAG,QAAQ,aAAa;IAC5D,SAAS;KACP,iBAAiB,UAAU;KAC3B,UAAU;KACV,iBAAiB;KAClB;IACD,QAAQ,YAAY,QAAQ,KAAM;IACnC,CAAC;AAEF,OAAI,CAAC,WAAW,IAAI;IAClB,MAAM,OAAO,MAAM,WAAW,MAAM,CAAC,aAAa,EAAE,EAAE;IACtD,MAAM,UAAU,MAAM,OAAO;AAE7B,QAAI,WAAW,WAAW,IACxB,QAAO,EACL,MAAM,mGAEP;AAGH,QAAI,WAAW,WAAW,OAAO,YAAY,oBAC3C,QAAO,EACL,MAAM,6JAGP;AAGH,QAAI,WAAW,WAAW,IACxB,QAAO,EACL,MAAM,0IAGP;AAGH,WAAO,EACL,MAAM,iBAAiB,WAAW,OAAO,KAAK,MAAM,OAAO,WAAW,WAAW,cAClF;;GAGH,MAAM,UAAU,MAAM,WAAW,MAAM;GACvC,MAAM,QAAQ,SAAS,SAAS;GAEhC,MAAM,QAAQ;IACZ;IACA;IACA,SAAS,MAAM,QAAQ;IACvB,OAAO,MAAM,MAAM;IACpB;AAED,OAAI,MAAM,YAAa,OAAM,KAAK,gBAAgB,MAAM,cAAc;AACtE,OAAI,MAAM,YAAa,OAAM,KAAK,SAAS,MAAM,cAAc;AAC/D,OAAI,MAAM,eAAgB,OAAM,KAAK,WAAW,MAAM,eAAe,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,eAAe,MAAM,GAAG,GAAG;AACvH,OAAI,MAAM,OAAQ,OAAM,KAAK,WAAW,MAAM,SAAS;AACvD,OAAI,MAAM,oBAAoB,KAAA,EAAW,OAAM,KAAK,aAAa,MAAM,gBAAgB,MAAM;AAG7F,OAAI;IACF,MAAM,UAAU,MAAM,aAAa,GAAG,QAAQ,iBAAiB;KAC7D,SAAS;MACP,iBAAiB,UAAU;MAC3B,UAAU;MACX;KACD,QAAQ,YAAY,QAAQ,IAAM;KACnC,CAAC;AACF,QAAI,QAAQ,IAAI;KACb,MAAM,WAAW,MAAM,QAAQ,MAAM;KACrC,MAAM,QAAQ,UAAU,eAAe,UAAU,UAAU,SAAS;AACrE,SAAI,QAAQ,GAAG;AACb,YAAM,KAAK,GAAG;AACd,YAAM,KAAK,yBAAyB,QAAQ;;;WAG1C;AAKR,OAAI;IACF,MAAM,YAAY,MAAM,aAAa,GAAG,QAAQ,UAAU;KACxD,SAAS;MACP,iBAAiB,UAAU;MAC3B,UAAU;MACX;KACD,QAAQ,YAAY,QAAQ,IAAM;KACnC,CAAC;AACF,QAAI,UAAU,IAAI;KAEf,MAAM,UADa,MAAM,UAAU,MAAM,GACd,UAAU,EAAE;AACxC,SAAI,OAAO,SAAS,EAClB,OAAM,KAAK,kBAAkB,OAAO,SAAS;;WAG3C;AAIR,SAAM,KAAK,GAAG;AACd,SAAM,KAAK,2FAAuF;AAElG,UAAO,EAAE,MAAM,MAAM,KAAK,KAAK,EAAE;WAC1B,KAAU;GACjB,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,OAAI,IAAI,SAAS,eAAe,IAAI,IAAI,SAAS,YAAY,IAAI,IAAI,SAAS,eAAe,IAAI,IAAI,SAAS,QAAQ,CACpH,QAAO,EACL,MAAM,2GAGP;AAEH,UAAO,EAAE,MAAM,iBAAiB,OAAO;;;CAG5C"}