@fml-inc/fml 0.0.28 → 0.0.30

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.
@@ -3,7 +3,7 @@ import {
3
3
  createApiClient,
4
4
  ensureNativeModules,
5
5
  getAuthenticatedClient
6
- } from "../chunk-ECXIFGGY.js";
6
+ } from "../chunk-RJOFOTNH.js";
7
7
  import "../chunk-UALBZA5X.js";
8
8
  import {
9
9
  AUTH_STORE_PATH,
@@ -12,7 +12,7 @@ import {
12
12
  getValidToken,
13
13
  initSentry,
14
14
  readTokens
15
- } from "../chunk-USJO3G5V.js";
15
+ } from "../chunk-TXQRKU2Y.js";
16
16
  import {
17
17
  parsePanopticonRunning
18
18
  } from "../chunk-YIWT7BWN.js";
@@ -235,6 +235,12 @@ function registerTools(server2) {
235
235
  mastraId: "integration-freshdesk",
236
236
  description: "Query and manage customer support tickets in Freshdesk. Common endpoints: 'tickets' (GET), 'tickets/{id}' (GET).",
237
237
  endpointHelp: "Freshdesk REST API v2 endpoint, e.g. 'tickets', 'tickets/{id}'"
238
+ },
239
+ {
240
+ name: "fml_query_stripe",
241
+ mastraId: "integration-stripe",
242
+ description: "Query Stripe API for customers, subscriptions, charges, invoices, and revenue. Common endpoints: 'customers' (GET), 'subscriptions?status=active' (GET), 'charges' (GET), 'invoices' (GET), 'balance' (GET).",
243
+ endpointHelp: "Stripe REST API v1 endpoint, e.g. 'customers', 'subscriptions?status=active', 'charges', 'balance'"
238
244
  }
239
245
  ];
240
246
  for (const tool of integrationTools) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { ensureNativeModules } from \"../native-check.js\";\nensureNativeModules();\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { registerTools } from \"./tools.js\";\nimport { getValidToken, readTokens } from \"../auth/token-store.js\";\nimport { AUTH_STORE_PATH } from \"../config.js\";\nimport { initSentry, Sentry } from \"../sentry.js\";\nimport { parsePanopticonRunning } from \"../commands/daemon.js\";\nimport { FML_DATA_DIR, FML_LOG_DIR } from \"../dirs.js\";\n\nconst LOG_PATH = path.join(FML_LOG_DIR, \"mcp.log\");\n\nconst server = new McpServer({\n name: \"fml\",\n version: \"0.1.0\",\n});\n\nregisterTools(server);\n\nasync function main() {\n await initSentry();\n\n // Redirect stderr to log file (stdout is reserved for MCP JSON-RPC protocol)\n if (!fs.existsSync(FML_LOG_DIR)) {\n fs.mkdirSync(FML_LOG_DIR, { recursive: true });\n }\n const logFd = fs.openSync(LOG_PATH, \"a\");\n const logStream = fs.createWriteStream(\"\", { fd: logFd });\n process.stderr.write = logStream.write.bind(\n logStream,\n ) as typeof process.stderr.write;\n\n // Check auth state and surface setup prompts\n const tokens = readTokens();\n const tokenValid = tokens ? await getValidToken() : null;\n\n if (!tokens) {\n try {\n const files = fs.readdirSync(FML_DATA_DIR).join(\", \") || \"(empty)\";\n console.error(\n `[fml] No auth file at ${AUTH_STORE_PATH}. Dir contents: ${files}`,\n );\n } catch {\n console.error(\n `[fml] No auth file at ${AUTH_STORE_PATH}. Dir not readable.`,\n );\n }\n server.resource(\"setup\", \"fml://setup\", async () => ({\n contents: [\n {\n uri: \"fml://setup\",\n text: \"FML plugin is not authenticated. Run `fml login` in your terminal to sign in, then restart Claude Code.\",\n },\n ],\n }));\n console.error(\"[fml] Not authenticated — run `fml login`\");\n } else if (!tokenValid) {\n console.error(\"[fml] Token expired — run `fml login` to re-authenticate\");\n }\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n // Ensure panopticon is running (includes sync, which handles config snapshots)\n if (!parsePanopticonRunning()) {\n try {\n const { handleStart } = await import(\"../commands/daemon.js\");\n await handleStart();\n } catch {\n console.error(\n \"[fml] Warning: could not start panopticon. Run `fml start` manually\",\n );\n }\n }\n}\n\nmain().catch((err) => {\n Sentry.captureException(err);\n console.error(\"FML MCP server error:\", err);\n process.exit(1);\n});\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n getSelectedOrg,\n getValidToken,\n readTokens,\n} from \"../auth/token-store.js\";\nimport { createApiClient, getAuthenticatedClient } from \"../convex-client.js\";\nimport { Sentry } from \"../sentry.js\";\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction textResult(data: unknown) {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }],\n };\n}\n\nfunction errorResult(message: string) {\n return {\n content: [{ type: \"text\" as const, text: message }],\n isError: true,\n };\n}\n\nconst PANOPTICON_HINT =\n \"\\n\\nFor local data, use the panopticon MCP tools instead (e.g. panopticon__sessions, panopticon__costs, panopticon__search, panopticon__timeline).\";\n\nasync function toolHandler(toolName: string, args: Record<string, unknown>) {\n try {\n const api = await getAuthenticatedClient();\n if (!api) {\n return errorResult(\n \"Not authenticated. Run `fml login` to sign in, then restart Claude Code.\" +\n PANOPTICON_HINT,\n );\n }\n const result = await api.callBackend(toolName, args);\n if (!result.ok) {\n return errorResult((result.error ?? \"Unknown error\") + PANOPTICON_HINT);\n }\n return textResult(result.result);\n } catch (err) {\n Sentry.captureException(err);\n const msg = err instanceof Error ? err.message : String(err);\n return errorResult(`Unexpected error: ${msg}` + PANOPTICON_HINT);\n }\n}\n\n// ─── Registration ─────────────────────────────────────────────────────────────\n\nexport function registerTools(server: McpServer): void {\n server.tool(\n \"whoami\",\n \"Show current authenticated user and available organizations\",\n {},\n async () => {\n const tokens = readTokens();\n const token = tokens ? await getValidToken() : null;\n let orgs = null;\n if (token) {\n try {\n orgs = await createApiClient(token).queryOrgs();\n } catch {}\n }\n return textResult({\n authenticated: !!tokens,\n user: tokens?.user ?? null,\n orgs,\n });\n },\n );\n\n // ── Messages ───────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_list_messages\",\n \"List messages from a conversation within a time range. Requires a conversationId from the FML web app.\",\n {\n startTime: z\n .number()\n .optional()\n .describe(\n \"Start time in milliseconds (inclusive). Omit to start from beginning.\",\n ),\n endTime: z\n .number()\n .optional()\n .describe(\n \"End time in milliseconds (inclusive). Omit to include up to most recent.\",\n ),\n limit: z\n .number()\n .optional()\n .describe(\n \"Maximum number of messages to return per page (default: 20)\",\n ),\n cursor: z\n .string()\n .optional()\n .describe(\"Pagination cursor from previous result to get next page\"),\n },\n async (args) => toolHandler(\"list-messages\", args),\n );\n\n server.tool(\n \"fml_list_messages_with_context\",\n \"Get messages around a specific message to see the context. Takes a message ID and returns messages before and after it.\",\n {\n messageId: z\n .string()\n .describe(\"The ID of the target message to get context around\"),\n before: z\n .number()\n .optional()\n .describe(\n \"Number of messages to retrieve before the target (default: 5)\",\n ),\n after: z\n .number()\n .optional()\n .describe(\n \"Number of messages to retrieve after the target (default: 5)\",\n ),\n },\n async (args) => toolHandler(\"list-messages-with-context\", args),\n );\n\n // ── Slack ──────────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_get_slack_channel_history\",\n \"Fetch recent message history from a Slack channel. Requires Slack integration to be connected.\",\n {\n channelId: z\n .string()\n .optional()\n .describe(\n \"Slack channel ID (e.g. C0123ABC). If omitted, uses the channel from the current Slack-sourced conversation.\",\n ),\n limit: z\n .number()\n .optional()\n .describe(\"Number of messages to fetch (default 20, max 50)\"),\n },\n async (args) => toolHandler(\"get-slack-channel-history\", args),\n );\n\n server.tool(\n \"fml_get_slack_message\",\n \"Fetch a specific Slack message by timestamp or permalink. Requires Slack integration to be connected.\",\n {\n permalink: z\n .string()\n .optional()\n .describe(\n \"Slack message permalink. If provided, channelId and messageTs are ignored.\",\n ),\n channelId: z\n .string()\n .optional()\n .describe(\n \"Slack channel ID (e.g. C0123ABC). Required if permalink not provided.\",\n ),\n messageTs: z\n .string()\n .optional()\n .describe(\n \"Message timestamp (e.g. 1234567890.123456). Required if permalink not provided.\",\n ),\n includeThreadContext: z\n .boolean()\n .optional()\n .describe(\n \"If true, fetches thread replies along with the message (default false)\",\n ),\n },\n async (args) => toolHandler(\"get-slack-message\", args),\n );\n\n // ── Skills ─────────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_list_skills\",\n \"List all available FML skills. Skills provide detailed knowledge about FML features.\",\n {},\n async () => toolHandler(\"list-skills\", {}),\n );\n\n server.tool(\n \"fml_load_skill\",\n \"Load a skill prompt by ID. Skills provide detailed knowledge about FML features.\",\n {\n skillId: z\n .string()\n .describe(\"The skill ID to load (e.g., 'fml-help', 'fml-navigation')\"),\n },\n async (args) => toolHandler(\"load-skill\", args),\n );\n\n // ── Analysis ───────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_search_analysis\",\n \"Search analysis results. USE FOR: finding analyses by topic, type, or status. RETURNS: full analysis content.\",\n {\n query: z\n .string()\n .optional()\n .describe(\"Text search across analysis type and content\"),\n promptKey: z\n .string()\n .optional()\n .describe(\"Exact filter by analysis type (e.g., 'analyze-codebase')\"),\n status: z\n .enum([\"complete\", \"running\", \"created\", \"failed\"])\n .optional()\n .describe(\"Filter by status\"),\n limit: z\n .number()\n .optional()\n .describe(\"Maximum number of results to return (default 20)\"),\n },\n async (args) => toolHandler(\"search-analysis\", args),\n );\n\n server.tool(\n \"fml_run_analysis_workflow\",\n \"Run comprehensive codebase analysis workflows. Available prompts: deep_security_auditor, deep_architecture_auditor, deep_code_quality_auditor, deep_performance_auditor, deep_ux_auditor, deep_dependencies_auditor, deep_cost_auditor, deep_ai_architecture_integration, deep_ai_security.\",\n {\n selectedPromptKeys: z\n .array(\n z.enum([\n \"deep_security_auditor\",\n \"deep_architecture_auditor\",\n \"deep_code_quality_auditor\",\n \"deep_performance_auditor\",\n \"deep_ux_auditor\",\n \"deep_dependencies_auditor\",\n \"deep_cost_auditor\",\n \"deep_ai_architecture_integration\",\n \"deep_ai_security\",\n ]),\n )\n .optional()\n .describe(\n \"Array of specific analysis prompts to run. If not provided, runs default set.\",\n ),\n },\n async (args) => toolHandler(\"run-analysis-workflow\", args),\n );\n\n // ── Integrations ───────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_list_integrations\",\n \"List integrations for this project — both connected and available. Use to check what integrations are active.\",\n {},\n async () => toolHandler(\"list-integrations\", {}),\n );\n\n // Integration query tools\n const integrationTools: Array<{\n name: string;\n mastraId: string;\n description: string;\n endpointHelp: string;\n }> = [\n {\n name: \"fml_query_sentry\",\n mastraId: \"integration-sentry\",\n description:\n \"Look up production errors, exceptions, and crash data from Sentry. Common endpoints: 'organizations/{orgSlug}/issues/' (GET), 'issues/{issue_id}/' (GET). {orgSlug} is auto-substituted.\",\n endpointHelp:\n \"Sentry REST API endpoint. {orgSlug} is auto-substituted. e.g. 'organizations/{orgSlug}/issues/'\",\n },\n {\n name: \"fml_query_slack\",\n mastraId: \"integration-slack\",\n description:\n \"Send messages and read channel/user info from Slack. Use channel IDs not names. Common endpoints: 'conversations.list' (GET), 'chat.postMessage' (POST).\",\n endpointHelp:\n \"Slack API method, e.g. 'conversations.list', 'chat.postMessage'\",\n },\n {\n name: \"fml_query_github\",\n mastraId: \"integration-github\",\n description:\n \"Query repository activity from GitHub — PRs, commits, issues, code. {owner} and {repo} are auto-substituted. Common endpoints: 'repos/{owner}/{repo}/pulls' (GET).\",\n endpointHelp: \"GitHub REST API path. e.g. 'repos/{owner}/{repo}/pulls'\",\n },\n {\n name: \"fml_query_linear\",\n mastraId: \"integration-linear\",\n description:\n \"Query and manage Linear issues and projects via GraphQL. Endpoint is always 'graphql', method POST.\",\n endpointHelp:\n \"Linear GraphQL endpoint — always use 'graphql' with method POST and {query, variables} in body\",\n },\n {\n name: \"fml_query_notion\",\n mastraId: \"integration-notion\",\n description:\n \"Search pages, query databases, read and write content in Notion. Common endpoints: 'search' (POST), 'databases/{id}/query' (POST).\",\n endpointHelp:\n \"Notion REST API endpoint, e.g. 'search', 'databases/{database_id}/query'\",\n },\n {\n name: \"fml_query_freshdesk\",\n mastraId: \"integration-freshdesk\",\n description:\n \"Query and manage customer support tickets in Freshdesk. Common endpoints: 'tickets' (GET), 'tickets/{id}' (GET).\",\n endpointHelp:\n \"Freshdesk REST API v2 endpoint, e.g. 'tickets', 'tickets/{id}'\",\n },\n ];\n\n for (const tool of integrationTools) {\n server.tool(\n tool.name,\n tool.description,\n {\n endpoint: z.string().describe(tool.endpointHelp),\n method: z\n .enum([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"])\n .optional()\n .describe(\"HTTP method. Defaults to GET.\"),\n body: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\"Request body as a JSON object (for POST/PUT/PATCH)\"),\n projectId: z\n .string()\n .optional()\n .describe(\n \"Optional project ID to scope this query. Required for GitHub (needs repo context).\",\n ),\n },\n async (args) => toolHandler(tool.mastraId, args),\n );\n }\n\n // ── Engineering Activity ───────────────────────────────────────────────────\n\n server.tool(\n \"get_engineering_activity\",\n \"Get agent activity summary (Claude Code, Codex, Gemini CLI, Mastra). SCOPES: 'org' (default), 'project', or 'user' (by githubUsername).\",\n {\n timeRange: z\n .enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"])\n .optional()\n .describe(\"Preset time range. Default '7d'.\"),\n since: z\n .string()\n .optional()\n .describe(\"ISO 8601 start timestamp. Overrides timeRange.\"),\n until: z.string().optional().describe(\"ISO 8601 end timestamp.\"),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n scope: z\n .enum([\"org\", \"project\", \"user\"])\n .optional()\n .describe(\"Query scope. 'org' (default), 'project', or 'user'.\"),\n githubUsername: z\n .string()\n .optional()\n .describe(\"Required when scope is 'user'. GitHub username.\"),\n },\n async (args) => toolHandler(\"get-engineering-activity\", args),\n );\n\n server.tool(\n \"list_engineering_sessions\",\n \"List agent sessions (Claude Code, Codex, Gemini CLI, Mastra). Each session includes first prompt preview and event counts.\",\n {\n scope: z\n .enum([\"org\", \"project\"])\n .optional()\n .describe(\"Query scope. Default 'org'.\"),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n githubUsername: z\n .string()\n .optional()\n .describe(\"Filter to sessions by this GitHub username.\"),\n timeRange: z.enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"]).optional(),\n since: z.string().optional().describe(\"ISO 8601 start timestamp.\"),\n until: z.string().optional().describe(\"ISO 8601 end timestamp.\"),\n limit: z\n .number()\n .optional()\n .describe(\"Max sessions to return (default 20, max 50).\"),\n },\n async (args) => toolHandler(\"list-engineering-sessions\", args),\n );\n\n server.tool(\n \"get_session_timeline\",\n 'Get chronological timeline for a coding agent session. Returns messages and/or hook events interleaved by timestamp. Use source=\"hooks\" to see permission requests, tool approvals, etc. REQUIRES: sessionId from list_engineering_sessions.',\n {\n sessionId: z.string().describe(\"The session ID to get details for.\"),\n includeSystemMessages: z\n .boolean()\n .optional()\n .describe(\"Include system messages (default false).\"),\n source: z\n .enum([\"messages\", \"hooks\", \"all\"])\n .optional()\n .describe(\n 'Filter by source: \"messages\" (scanner messages only), \"hooks\" (hook events only), \"all\" (both interleaved). Default \"all\".',\n ),\n eventType: z\n .string()\n .optional()\n .describe(\n 'Filter hook events by type (e.g. \"PermissionRequest\", \"PreToolUse\", \"Stop\"). Only applies when source includes hooks.',\n ),\n limit: z\n .number()\n .optional()\n .describe(\"Max entries to return (default 50, max 200).\"),\n offset: z\n .number()\n .optional()\n .describe(\"Skip first N entries for pagination.\"),\n },\n async (args) => toolHandler(\"get-session-timeline\", args),\n );\n\n server.tool(\n \"get_session_turns\",\n \"Get per-turn token accounting for a coding agent session. USE FOR: understanding cost per turn, model used at each step, cache hit analysis. REQUIRES: sessionId from list_engineering_sessions.\",\n {\n sessionId: z.string().describe(\"The session ID to get turns for.\"),\n limit: z\n .number()\n .optional()\n .describe(\"Max turns to return (default 50, max 200).\"),\n offset: z\n .number()\n .optional()\n .describe(\"Skip first N turns for pagination.\"),\n },\n async (args) => toolHandler(\"get-session-turns\", args),\n );\n\n server.tool(\n \"get_ai_spending\",\n \"Get AI spending and token usage data. Groups by 'session' (default), 'model', or 'day'.\",\n {\n timeRange: z\n .enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"])\n .optional()\n .describe(\"Default '7d'.\"),\n since: z.string().optional().describe(\"ISO 8601 start timestamp.\"),\n until: z.string().optional().describe(\"ISO 8601 end timestamp.\"),\n groupBy: z\n .enum([\"session\", \"model\", \"day\"])\n .optional()\n .describe(\"How to group results.\"),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n },\n async (args) => toolHandler(\"get-ai-spending\", args),\n );\n\n server.tool(\n \"search_engineering_sessions\",\n \"Search across agent sessions by text. Searches prompts, tool names, and event payloads.\",\n {\n query: z.string().describe(\"Text to search for. Case-insensitive.\"),\n timeRange: z.enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"]).optional(),\n since: z.string().optional(),\n until: z.string().optional(),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n limit: z.number().optional().describe(\"Max results (default 20).\"),\n },\n async (args) => toolHandler(\"search-engineering-sessions\", args),\n );\n\n // ── Config Snapshots ────────────────────────────────────────────────────\n\n server.tool(\n \"list_user_configs\",\n \"List user-level configuration snapshots (CLAUDE.md, settings, etc.) across the org. Shows who has configs and when they were last updated.\",\n {},\n async () => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.listUserConfigSnapshots(orgSlug);\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n\n server.tool(\n \"get_user_config\",\n \"Get detailed user configuration for a specific GitHub user — their CLAUDE.md, settings.json, MCP servers, etc.\",\n {\n githubUsername: z.string().describe(\"GitHub username to look up.\"),\n },\n async (args) => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.getUserConfigDetail(\n orgSlug,\n args.githubUsername,\n );\n if (!result)\n return errorResult(`No config found for ${args.githubUsername}.`);\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n\n server.tool(\n \"list_repo_configs\",\n \"List repository-level configuration snapshots (CLAUDE.md, .mcp.json, etc.) across the org. Optionally filter by repository.\",\n {\n repository: z\n .string()\n .optional()\n .describe(\"Filter by repository full name (e.g. 'org/repo').\"),\n },\n async (args) => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.listRepoConfigSnapshots(\n orgSlug,\n args.repository,\n );\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n\n server.tool(\n \"get_repo_config\",\n \"Get detailed repository configuration — CLAUDE.md files, .mcp.json, and other agent config found in a specific repo.\",\n {\n repository: z\n .string()\n .describe(\"Repository full name (e.g. 'org/repo').\"),\n },\n async (args) => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.getRepoConfigDetail(orgSlug, args.repository);\n if (!result)\n return errorResult(`No config found for ${args.repository}.`);\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACPrC,SAAS,SAAS;AAWlB,SAAS,WAAW,MAAe;AACjC,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC;AAAA,EAC1E;AACF;AAEA,SAAS,YAAY,SAAiB;AACpC,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC;AAAA,IAClD,SAAS;AAAA,EACX;AACF;AAEA,IAAM,kBACJ;AAEF,eAAe,YAAY,UAAkB,MAA+B;AAC1E,MAAI;AACF,UAAM,MAAM,MAAM,uBAAuB;AACzC,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,QACL,6EACE;AAAA,MACJ;AAAA,IACF;AACA,UAAM,SAAS,MAAM,IAAI,YAAY,UAAU,IAAI;AACnD,QAAI,CAAC,OAAO,IAAI;AACd,aAAO,aAAa,OAAO,SAAS,mBAAmB,eAAe;AAAA,IACxE;AACA,WAAO,WAAW,OAAO,MAAM;AAAA,EACjC,SAAS,KAAK;AACZ,WAAO,iBAAiB,GAAG;AAC3B,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,WAAO,YAAY,qBAAqB,GAAG,KAAK,eAAe;AAAA,EACjE;AACF;AAIO,SAAS,cAAcA,SAAyB;AACrD,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY;AACV,YAAM,SAAS,WAAW;AAC1B,YAAM,QAAQ,SAAS,MAAM,cAAc,IAAI;AAC/C,UAAI,OAAO;AACX,UAAI,OAAO;AACT,YAAI;AACF,iBAAO,MAAM,gBAAgB,KAAK,EAAE,UAAU;AAAA,QAChD,QAAQ;AAAA,QAAC;AAAA,MACX;AACA,aAAO,WAAW;AAAA,QAChB,eAAe,CAAC,CAAC;AAAA,QACjB,MAAM,QAAQ,QAAQ;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,SAAS,EACN,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,QAAQ,EACL,OAAO,EACP,SAAS,EACT,SAAS,yDAAyD;AAAA,IACvE;AAAA,IACA,OAAO,SAAS,YAAY,iBAAiB,IAAI;AAAA,EACnD;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EACR,OAAO,EACP,SAAS,oDAAoD;AAAA,MAChE,QAAQ,EACL,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ;AAAA,IACA,OAAO,SAAS,YAAY,8BAA8B,IAAI;AAAA,EAChE;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,kDAAkD;AAAA,IAChE;AAAA,IACA,OAAO,SAAS,YAAY,6BAA6B,IAAI;AAAA,EAC/D;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,sBAAsB,EACnB,QAAQ,EACR,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ;AAAA,IACA,OAAO,SAAS,YAAY,qBAAqB,IAAI;AAAA,EACvD;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY,YAAY,eAAe,CAAC,CAAC;AAAA,EAC3C;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,SAAS,EACN,OAAO,EACP,SAAS,2DAA2D;AAAA,IACzE;AAAA,IACA,OAAO,SAAS,YAAY,cAAc,IAAI;AAAA,EAChD;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,8CAA8C;AAAA,MAC1D,WAAW,EACR,OAAO,EACP,SAAS,EACT,SAAS,0DAA0D;AAAA,MACtE,QAAQ,EACL,KAAK,CAAC,YAAY,WAAW,WAAW,QAAQ,CAAC,EACjD,SAAS,EACT,SAAS,kBAAkB;AAAA,MAC9B,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,kDAAkD;AAAA,IAChE;AAAA,IACA,OAAO,SAAS,YAAY,mBAAmB,IAAI;AAAA,EACrD;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,oBAAoB,EACjB;AAAA,QACC,EAAE,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,EACC,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ;AAAA,IACA,OAAO,SAAS,YAAY,yBAAyB,IAAI;AAAA,EAC3D;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY,YAAY,qBAAqB,CAAC,CAAC;AAAA,EACjD;AAGA,QAAM,mBAKD;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MACF,cACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MACF,cACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MACF,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MACF,cACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MACF,cACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MACF,cACE;AAAA,IACJ;AAAA,EACF;AAEA,aAAW,QAAQ,kBAAkB;AACnC,IAAAA,QAAO;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL;AAAA,QACE,UAAU,EAAE,OAAO,EAAE,SAAS,KAAK,YAAY;AAAA,QAC/C,QAAQ,EACL,KAAK,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ,CAAC,EAC9C,SAAS,EACT,SAAS,+BAA+B;AAAA,QAC3C,MAAM,EACH,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAC9B,SAAS,EACT,SAAS,oDAAoD;AAAA,QAChE,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,MACJ;AAAA,MACA,OAAO,SAAS,YAAY,KAAK,UAAU,IAAI;AAAA,IACjD;AAAA,EACF;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EACR,KAAK,CAAC,MAAM,MAAM,OAAO,MAAM,KAAK,CAAC,EACrC,SAAS,EACT,SAAS,kCAAkC;AAAA,MAC9C,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,gDAAgD;AAAA,MAC5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,MAC/D,QAAQ,EACL,KAAK,CAAC,UAAU,SAAS,UAAU,QAAQ,CAAC,EAC5C,SAAS,EACT,SAAS,qCAAqC;AAAA,MACjD,OAAO,EACJ,KAAK,CAAC,OAAO,WAAW,MAAM,CAAC,EAC/B,SAAS,EACT,SAAS,qDAAqD;AAAA,MACjE,gBAAgB,EACb,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAAA,IAC/D;AAAA,IACA,OAAO,SAAS,YAAY,4BAA4B,IAAI;AAAA,EAC9D;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,EACJ,KAAK,CAAC,OAAO,SAAS,CAAC,EACvB,SAAS,EACT,SAAS,6BAA6B;AAAA,MACzC,QAAQ,EACL,KAAK,CAAC,UAAU,SAAS,UAAU,QAAQ,CAAC,EAC5C,SAAS,EACT,SAAS,qCAAqC;AAAA,MACjD,gBAAgB,EACb,OAAO,EACP,SAAS,EACT,SAAS,6CAA6C;AAAA,MACzD,WAAW,EAAE,KAAK,CAAC,MAAM,MAAM,OAAO,MAAM,KAAK,CAAC,EAAE,SAAS;AAAA,MAC7D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,MACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,MAC/D,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,8CAA8C;AAAA,IAC5D;AAAA,IACA,OAAO,SAAS,YAAY,6BAA6B,IAAI;AAAA,EAC/D;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EAAE,OAAO,EAAE,SAAS,oCAAoC;AAAA,MACnE,uBAAuB,EACpB,QAAQ,EACR,SAAS,EACT,SAAS,0CAA0C;AAAA,MACtD,QAAQ,EACL,KAAK,CAAC,YAAY,SAAS,KAAK,CAAC,EACjC,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,8CAA8C;AAAA,MAC1D,QAAQ,EACL,OAAO,EACP,SAAS,EACT,SAAS,sCAAsC;AAAA,IACpD;AAAA,IACA,OAAO,SAAS,YAAY,wBAAwB,IAAI;AAAA,EAC1D;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EAAE,OAAO,EAAE,SAAS,kCAAkC;AAAA,MACjE,OAAO,EACJ,OAAO,EACP,SAAS,EACT,SAAS,4CAA4C;AAAA,MACxD,QAAQ,EACL,OAAO,EACP,SAAS,EACT,SAAS,oCAAoC;AAAA,IAClD;AAAA,IACA,OAAO,SAAS,YAAY,qBAAqB,IAAI;AAAA,EACvD;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EACR,KAAK,CAAC,MAAM,MAAM,OAAO,MAAM,KAAK,CAAC,EACrC,SAAS,EACT,SAAS,eAAe;AAAA,MAC3B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,MACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,MAC/D,SAAS,EACN,KAAK,CAAC,WAAW,SAAS,KAAK,CAAC,EAChC,SAAS,EACT,SAAS,uBAAuB;AAAA,MACnC,QAAQ,EACL,KAAK,CAAC,UAAU,SAAS,UAAU,QAAQ,CAAC,EAC5C,SAAS,EACT,SAAS,qCAAqC;AAAA,IACnD;AAAA,IACA,OAAO,SAAS,YAAY,mBAAmB,IAAI;AAAA,EACrD;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,uCAAuC;AAAA,MAClE,WAAW,EAAE,KAAK,CAAC,MAAM,MAAM,OAAO,MAAM,KAAK,CAAC,EAAE,SAAS;AAAA,MAC7D,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,MAC3B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,MAC3B,QAAQ,EACL,KAAK,CAAC,UAAU,SAAS,UAAU,QAAQ,CAAC,EAC5C,SAAS,EACT,SAAS,qCAAqC;AAAA,MACjD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,IACnE;AAAA,IACA,OAAO,SAAS,YAAY,+BAA+B,IAAI;AAAA,EACjE;AAIA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY;AACV,UAAI;AACF,cAAM,UAAU,eAAe;AAC/B,YAAI,CAAC,QAAS,QAAO,YAAY,mCAAmC;AACpE,cAAM,MAAM,MAAM,uBAAuB;AACzC,YAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,cAAM,SAAS,MAAM,IAAI,wBAAwB,OAAO;AACxD,eAAO,WAAW,MAAM;AAAA,MAC1B,SAAS,KAAK;AACZ,eAAO,iBAAiB,GAAG;AAC3B,eAAO;AAAA,UACL,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,gBAAgB,EAAE,OAAO,EAAE,SAAS,6BAA6B;AAAA,IACnE;AAAA,IACA,OAAO,SAAS;AACd,UAAI;AACF,cAAM,UAAU,eAAe;AAC/B,YAAI,CAAC,QAAS,QAAO,YAAY,mCAAmC;AACpE,cAAM,MAAM,MAAM,uBAAuB;AACzC,YAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,cAAM,SAAS,MAAM,IAAI;AAAA,UACvB;AAAA,UACA,KAAK;AAAA,QACP;AACA,YAAI,CAAC;AACH,iBAAO,YAAY,uBAAuB,KAAK,cAAc,GAAG;AAClE,eAAO,WAAW,MAAM;AAAA,MAC1B,SAAS,KAAK;AACZ,eAAO,iBAAiB,GAAG;AAC3B,eAAO;AAAA,UACL,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAY,EACT,OAAO,EACP,SAAS,EACT,SAAS,mDAAmD;AAAA,IACjE;AAAA,IACA,OAAO,SAAS;AACd,UAAI;AACF,cAAM,UAAU,eAAe;AAC/B,YAAI,CAAC,QAAS,QAAO,YAAY,mCAAmC;AACpE,cAAM,MAAM,MAAM,uBAAuB;AACzC,YAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,cAAM,SAAS,MAAM,IAAI;AAAA,UACvB;AAAA,UACA,KAAK;AAAA,QACP;AACA,eAAO,WAAW,MAAM;AAAA,MAC1B,SAAS,KAAK;AACZ,eAAO,iBAAiB,GAAG;AAC3B,eAAO;AAAA,UACL,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAY,EACT,OAAO,EACP,SAAS,yCAAyC;AAAA,IACvD;AAAA,IACA,OAAO,SAAS;AACd,UAAI;AACF,cAAM,UAAU,eAAe;AAC/B,YAAI,CAAC,QAAS,QAAO,YAAY,mCAAmC;AACpE,cAAM,MAAM,MAAM,uBAAuB;AACzC,YAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,cAAM,SAAS,MAAM,IAAI,oBAAoB,SAAS,KAAK,UAAU;AACrE,YAAI,CAAC;AACH,iBAAO,YAAY,uBAAuB,KAAK,UAAU,GAAG;AAC9D,eAAO,WAAW,MAAM;AAAA,MAC1B,SAAS,KAAK;AACZ,eAAO,iBAAiB,GAAG;AAC3B,eAAO;AAAA,UACL,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ADhlBA,oBAAoB;AAapB,IAAM,WAAW,KAAK,KAAK,aAAa,SAAS;AAEjD,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS;AACX,CAAC;AAED,cAAc,MAAM;AAEpB,eAAe,OAAO;AACpB,QAAM,WAAW;AAGjB,MAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B,OAAG,UAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,EAC/C;AACA,QAAM,QAAQ,GAAG,SAAS,UAAU,GAAG;AACvC,QAAM,YAAY,GAAG,kBAAkB,IAAI,EAAE,IAAI,MAAM,CAAC;AACxD,UAAQ,OAAO,QAAQ,UAAU,MAAM;AAAA,IACrC;AAAA,EACF;AAGA,QAAM,SAAS,WAAW;AAC1B,QAAM,aAAa,SAAS,MAAM,cAAc,IAAI;AAEpD,MAAI,CAAC,QAAQ;AACX,QAAI;AACF,YAAM,QAAQ,GAAG,YAAY,YAAY,EAAE,KAAK,IAAI,KAAK;AACzD,cAAQ;AAAA,QACN,yBAAyB,eAAe,mBAAmB,KAAK;AAAA,MAClE;AAAA,IACF,QAAQ;AACN,cAAQ;AAAA,QACN,yBAAyB,eAAe;AAAA,MAC1C;AAAA,IACF;AACA,WAAO,SAAS,SAAS,eAAe,aAAa;AAAA,MACnD,UAAU;AAAA,QACR;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF,EAAE;AACF,YAAQ,MAAM,gDAA2C;AAAA,EAC3D,WAAW,CAAC,YAAY;AACtB,YAAQ,MAAM,+DAA0D;AAAA,EAC1E;AAEA,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAG9B,MAAI,CAAC,uBAAuB,GAAG;AAC7B,QAAI;AACF,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,uBAAuB;AAC5D,YAAM,YAAY;AAAA,IACpB,QAAQ;AACN,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,SAAO,iBAAiB,GAAG;AAC3B,UAAQ,MAAM,yBAAyB,GAAG;AAC1C,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["server"]}
1
+ {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { ensureNativeModules } from \"../native-check.js\";\nensureNativeModules();\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { registerTools } from \"./tools.js\";\nimport { getValidToken, readTokens } from \"../auth/token-store.js\";\nimport { AUTH_STORE_PATH } from \"../config.js\";\nimport { initSentry, Sentry } from \"../sentry.js\";\nimport { parsePanopticonRunning } from \"../commands/daemon.js\";\nimport { FML_DATA_DIR, FML_LOG_DIR } from \"../dirs.js\";\n\nconst LOG_PATH = path.join(FML_LOG_DIR, \"mcp.log\");\n\nconst server = new McpServer({\n name: \"fml\",\n version: \"0.1.0\",\n});\n\nregisterTools(server);\n\nasync function main() {\n await initSentry();\n\n // Redirect stderr to log file (stdout is reserved for MCP JSON-RPC protocol)\n if (!fs.existsSync(FML_LOG_DIR)) {\n fs.mkdirSync(FML_LOG_DIR, { recursive: true });\n }\n const logFd = fs.openSync(LOG_PATH, \"a\");\n const logStream = fs.createWriteStream(\"\", { fd: logFd });\n process.stderr.write = logStream.write.bind(\n logStream,\n ) as typeof process.stderr.write;\n\n // Check auth state and surface setup prompts\n const tokens = readTokens();\n const tokenValid = tokens ? await getValidToken() : null;\n\n if (!tokens) {\n try {\n const files = fs.readdirSync(FML_DATA_DIR).join(\", \") || \"(empty)\";\n console.error(\n `[fml] No auth file at ${AUTH_STORE_PATH}. Dir contents: ${files}`,\n );\n } catch {\n console.error(\n `[fml] No auth file at ${AUTH_STORE_PATH}. Dir not readable.`,\n );\n }\n server.resource(\"setup\", \"fml://setup\", async () => ({\n contents: [\n {\n uri: \"fml://setup\",\n text: \"FML plugin is not authenticated. Run `fml login` in your terminal to sign in, then restart Claude Code.\",\n },\n ],\n }));\n console.error(\"[fml] Not authenticated — run `fml login`\");\n } else if (!tokenValid) {\n console.error(\"[fml] Token expired — run `fml login` to re-authenticate\");\n }\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n // Ensure panopticon is running (includes sync, which handles config snapshots)\n if (!parsePanopticonRunning()) {\n try {\n const { handleStart } = await import(\"../commands/daemon.js\");\n await handleStart();\n } catch {\n console.error(\n \"[fml] Warning: could not start panopticon. Run `fml start` manually\",\n );\n }\n }\n}\n\nmain().catch((err) => {\n Sentry.captureException(err);\n console.error(\"FML MCP server error:\", err);\n process.exit(1);\n});\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n getSelectedOrg,\n getValidToken,\n readTokens,\n} from \"../auth/token-store.js\";\nimport { createApiClient, getAuthenticatedClient } from \"../convex-client.js\";\nimport { Sentry } from \"../sentry.js\";\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction textResult(data: unknown) {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }],\n };\n}\n\nfunction errorResult(message: string) {\n return {\n content: [{ type: \"text\" as const, text: message }],\n isError: true,\n };\n}\n\nconst PANOPTICON_HINT =\n \"\\n\\nFor local data, use the panopticon MCP tools instead (e.g. panopticon__sessions, panopticon__costs, panopticon__search, panopticon__timeline).\";\n\nasync function toolHandler(toolName: string, args: Record<string, unknown>) {\n try {\n const api = await getAuthenticatedClient();\n if (!api) {\n return errorResult(\n \"Not authenticated. Run `fml login` to sign in, then restart Claude Code.\" +\n PANOPTICON_HINT,\n );\n }\n const result = await api.callBackend(toolName, args);\n if (!result.ok) {\n return errorResult((result.error ?? \"Unknown error\") + PANOPTICON_HINT);\n }\n return textResult(result.result);\n } catch (err) {\n Sentry.captureException(err);\n const msg = err instanceof Error ? err.message : String(err);\n return errorResult(`Unexpected error: ${msg}` + PANOPTICON_HINT);\n }\n}\n\n// ─── Registration ─────────────────────────────────────────────────────────────\n\nexport function registerTools(server: McpServer): void {\n server.tool(\n \"whoami\",\n \"Show current authenticated user and available organizations\",\n {},\n async () => {\n const tokens = readTokens();\n const token = tokens ? await getValidToken() : null;\n let orgs = null;\n if (token) {\n try {\n orgs = await createApiClient(token).queryOrgs();\n } catch {}\n }\n return textResult({\n authenticated: !!tokens,\n user: tokens?.user ?? null,\n orgs,\n });\n },\n );\n\n // ── Messages ───────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_list_messages\",\n \"List messages from a conversation within a time range. Requires a conversationId from the FML web app.\",\n {\n startTime: z\n .number()\n .optional()\n .describe(\n \"Start time in milliseconds (inclusive). Omit to start from beginning.\",\n ),\n endTime: z\n .number()\n .optional()\n .describe(\n \"End time in milliseconds (inclusive). Omit to include up to most recent.\",\n ),\n limit: z\n .number()\n .optional()\n .describe(\n \"Maximum number of messages to return per page (default: 20)\",\n ),\n cursor: z\n .string()\n .optional()\n .describe(\"Pagination cursor from previous result to get next page\"),\n },\n async (args) => toolHandler(\"list-messages\", args),\n );\n\n server.tool(\n \"fml_list_messages_with_context\",\n \"Get messages around a specific message to see the context. Takes a message ID and returns messages before and after it.\",\n {\n messageId: z\n .string()\n .describe(\"The ID of the target message to get context around\"),\n before: z\n .number()\n .optional()\n .describe(\n \"Number of messages to retrieve before the target (default: 5)\",\n ),\n after: z\n .number()\n .optional()\n .describe(\n \"Number of messages to retrieve after the target (default: 5)\",\n ),\n },\n async (args) => toolHandler(\"list-messages-with-context\", args),\n );\n\n // ── Slack ──────────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_get_slack_channel_history\",\n \"Fetch recent message history from a Slack channel. Requires Slack integration to be connected.\",\n {\n channelId: z\n .string()\n .optional()\n .describe(\n \"Slack channel ID (e.g. C0123ABC). If omitted, uses the channel from the current Slack-sourced conversation.\",\n ),\n limit: z\n .number()\n .optional()\n .describe(\"Number of messages to fetch (default 20, max 50)\"),\n },\n async (args) => toolHandler(\"get-slack-channel-history\", args),\n );\n\n server.tool(\n \"fml_get_slack_message\",\n \"Fetch a specific Slack message by timestamp or permalink. Requires Slack integration to be connected.\",\n {\n permalink: z\n .string()\n .optional()\n .describe(\n \"Slack message permalink. If provided, channelId and messageTs are ignored.\",\n ),\n channelId: z\n .string()\n .optional()\n .describe(\n \"Slack channel ID (e.g. C0123ABC). Required if permalink not provided.\",\n ),\n messageTs: z\n .string()\n .optional()\n .describe(\n \"Message timestamp (e.g. 1234567890.123456). Required if permalink not provided.\",\n ),\n includeThreadContext: z\n .boolean()\n .optional()\n .describe(\n \"If true, fetches thread replies along with the message (default false)\",\n ),\n },\n async (args) => toolHandler(\"get-slack-message\", args),\n );\n\n // ── Skills ─────────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_list_skills\",\n \"List all available FML skills. Skills provide detailed knowledge about FML features.\",\n {},\n async () => toolHandler(\"list-skills\", {}),\n );\n\n server.tool(\n \"fml_load_skill\",\n \"Load a skill prompt by ID. Skills provide detailed knowledge about FML features.\",\n {\n skillId: z\n .string()\n .describe(\"The skill ID to load (e.g., 'fml-help', 'fml-navigation')\"),\n },\n async (args) => toolHandler(\"load-skill\", args),\n );\n\n // ── Analysis ───────────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_search_analysis\",\n \"Search analysis results. USE FOR: finding analyses by topic, type, or status. RETURNS: full analysis content.\",\n {\n query: z\n .string()\n .optional()\n .describe(\"Text search across analysis type and content\"),\n promptKey: z\n .string()\n .optional()\n .describe(\"Exact filter by analysis type (e.g., 'analyze-codebase')\"),\n status: z\n .enum([\"complete\", \"running\", \"created\", \"failed\"])\n .optional()\n .describe(\"Filter by status\"),\n limit: z\n .number()\n .optional()\n .describe(\"Maximum number of results to return (default 20)\"),\n },\n async (args) => toolHandler(\"search-analysis\", args),\n );\n\n server.tool(\n \"fml_run_analysis_workflow\",\n \"Run comprehensive codebase analysis workflows. Available prompts: deep_security_auditor, deep_architecture_auditor, deep_code_quality_auditor, deep_performance_auditor, deep_ux_auditor, deep_dependencies_auditor, deep_cost_auditor, deep_ai_architecture_integration, deep_ai_security.\",\n {\n selectedPromptKeys: z\n .array(\n z.enum([\n \"deep_security_auditor\",\n \"deep_architecture_auditor\",\n \"deep_code_quality_auditor\",\n \"deep_performance_auditor\",\n \"deep_ux_auditor\",\n \"deep_dependencies_auditor\",\n \"deep_cost_auditor\",\n \"deep_ai_architecture_integration\",\n \"deep_ai_security\",\n ]),\n )\n .optional()\n .describe(\n \"Array of specific analysis prompts to run. If not provided, runs default set.\",\n ),\n },\n async (args) => toolHandler(\"run-analysis-workflow\", args),\n );\n\n // ── Integrations ───────────────────────────────────────────────────────────\n\n server.tool(\n \"fml_list_integrations\",\n \"List integrations for this project — both connected and available. Use to check what integrations are active.\",\n {},\n async () => toolHandler(\"list-integrations\", {}),\n );\n\n // Integration query tools\n const integrationTools: Array<{\n name: string;\n mastraId: string;\n description: string;\n endpointHelp: string;\n }> = [\n {\n name: \"fml_query_sentry\",\n mastraId: \"integration-sentry\",\n description:\n \"Look up production errors, exceptions, and crash data from Sentry. Common endpoints: 'organizations/{orgSlug}/issues/' (GET), 'issues/{issue_id}/' (GET). {orgSlug} is auto-substituted.\",\n endpointHelp:\n \"Sentry REST API endpoint. {orgSlug} is auto-substituted. e.g. 'organizations/{orgSlug}/issues/'\",\n },\n {\n name: \"fml_query_slack\",\n mastraId: \"integration-slack\",\n description:\n \"Send messages and read channel/user info from Slack. Use channel IDs not names. Common endpoints: 'conversations.list' (GET), 'chat.postMessage' (POST).\",\n endpointHelp:\n \"Slack API method, e.g. 'conversations.list', 'chat.postMessage'\",\n },\n {\n name: \"fml_query_github\",\n mastraId: \"integration-github\",\n description:\n \"Query repository activity from GitHub — PRs, commits, issues, code. {owner} and {repo} are auto-substituted. Common endpoints: 'repos/{owner}/{repo}/pulls' (GET).\",\n endpointHelp: \"GitHub REST API path. e.g. 'repos/{owner}/{repo}/pulls'\",\n },\n {\n name: \"fml_query_linear\",\n mastraId: \"integration-linear\",\n description:\n \"Query and manage Linear issues and projects via GraphQL. Endpoint is always 'graphql', method POST.\",\n endpointHelp:\n \"Linear GraphQL endpoint — always use 'graphql' with method POST and {query, variables} in body\",\n },\n {\n name: \"fml_query_notion\",\n mastraId: \"integration-notion\",\n description:\n \"Search pages, query databases, read and write content in Notion. Common endpoints: 'search' (POST), 'databases/{id}/query' (POST).\",\n endpointHelp:\n \"Notion REST API endpoint, e.g. 'search', 'databases/{database_id}/query'\",\n },\n {\n name: \"fml_query_freshdesk\",\n mastraId: \"integration-freshdesk\",\n description:\n \"Query and manage customer support tickets in Freshdesk. Common endpoints: 'tickets' (GET), 'tickets/{id}' (GET).\",\n endpointHelp:\n \"Freshdesk REST API v2 endpoint, e.g. 'tickets', 'tickets/{id}'\",\n },\n {\n name: \"fml_query_stripe\",\n mastraId: \"integration-stripe\",\n description:\n \"Query Stripe API for customers, subscriptions, charges, invoices, and revenue. Common endpoints: 'customers' (GET), 'subscriptions?status=active' (GET), 'charges' (GET), 'invoices' (GET), 'balance' (GET).\",\n endpointHelp:\n \"Stripe REST API v1 endpoint, e.g. 'customers', 'subscriptions?status=active', 'charges', 'balance'\",\n },\n ];\n\n for (const tool of integrationTools) {\n server.tool(\n tool.name,\n tool.description,\n {\n endpoint: z.string().describe(tool.endpointHelp),\n method: z\n .enum([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"])\n .optional()\n .describe(\"HTTP method. Defaults to GET.\"),\n body: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\"Request body as a JSON object (for POST/PUT/PATCH)\"),\n projectId: z\n .string()\n .optional()\n .describe(\n \"Optional project ID to scope this query. Required for GitHub (needs repo context).\",\n ),\n },\n async (args) => toolHandler(tool.mastraId, args),\n );\n }\n\n // ── Engineering Activity ───────────────────────────────────────────────────\n\n server.tool(\n \"get_engineering_activity\",\n \"Get agent activity summary (Claude Code, Codex, Gemini CLI, Mastra). SCOPES: 'org' (default), 'project', or 'user' (by githubUsername).\",\n {\n timeRange: z\n .enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"])\n .optional()\n .describe(\"Preset time range. Default '7d'.\"),\n since: z\n .string()\n .optional()\n .describe(\"ISO 8601 start timestamp. Overrides timeRange.\"),\n until: z.string().optional().describe(\"ISO 8601 end timestamp.\"),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n scope: z\n .enum([\"org\", \"project\", \"user\"])\n .optional()\n .describe(\"Query scope. 'org' (default), 'project', or 'user'.\"),\n githubUsername: z\n .string()\n .optional()\n .describe(\"Required when scope is 'user'. GitHub username.\"),\n },\n async (args) => toolHandler(\"get-engineering-activity\", args),\n );\n\n server.tool(\n \"list_engineering_sessions\",\n \"List agent sessions (Claude Code, Codex, Gemini CLI, Mastra). Each session includes first prompt preview and event counts.\",\n {\n scope: z\n .enum([\"org\", \"project\"])\n .optional()\n .describe(\"Query scope. Default 'org'.\"),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n githubUsername: z\n .string()\n .optional()\n .describe(\"Filter to sessions by this GitHub username.\"),\n timeRange: z.enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"]).optional(),\n since: z.string().optional().describe(\"ISO 8601 start timestamp.\"),\n until: z.string().optional().describe(\"ISO 8601 end timestamp.\"),\n limit: z\n .number()\n .optional()\n .describe(\"Max sessions to return (default 20, max 50).\"),\n },\n async (args) => toolHandler(\"list-engineering-sessions\", args),\n );\n\n server.tool(\n \"get_session_timeline\",\n 'Get chronological timeline for a coding agent session. Returns messages and/or hook events interleaved by timestamp. Use source=\"hooks\" to see permission requests, tool approvals, etc. REQUIRES: sessionId from list_engineering_sessions.',\n {\n sessionId: z.string().describe(\"The session ID to get details for.\"),\n includeSystemMessages: z\n .boolean()\n .optional()\n .describe(\"Include system messages (default false).\"),\n source: z\n .enum([\"messages\", \"hooks\", \"all\"])\n .optional()\n .describe(\n 'Filter by source: \"messages\" (scanner messages only), \"hooks\" (hook events only), \"all\" (both interleaved). Default \"all\".',\n ),\n eventType: z\n .string()\n .optional()\n .describe(\n 'Filter hook events by type (e.g. \"PermissionRequest\", \"PreToolUse\", \"Stop\"). Only applies when source includes hooks.',\n ),\n limit: z\n .number()\n .optional()\n .describe(\"Max entries to return (default 50, max 200).\"),\n offset: z\n .number()\n .optional()\n .describe(\"Skip first N entries for pagination.\"),\n },\n async (args) => toolHandler(\"get-session-timeline\", args),\n );\n\n server.tool(\n \"get_session_turns\",\n \"Get per-turn token accounting for a coding agent session. USE FOR: understanding cost per turn, model used at each step, cache hit analysis. REQUIRES: sessionId from list_engineering_sessions.\",\n {\n sessionId: z.string().describe(\"The session ID to get turns for.\"),\n limit: z\n .number()\n .optional()\n .describe(\"Max turns to return (default 50, max 200).\"),\n offset: z\n .number()\n .optional()\n .describe(\"Skip first N turns for pagination.\"),\n },\n async (args) => toolHandler(\"get-session-turns\", args),\n );\n\n server.tool(\n \"get_ai_spending\",\n \"Get AI spending and token usage data. Groups by 'session' (default), 'model', or 'day'.\",\n {\n timeRange: z\n .enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"])\n .optional()\n .describe(\"Default '7d'.\"),\n since: z.string().optional().describe(\"ISO 8601 start timestamp.\"),\n until: z.string().optional().describe(\"ISO 8601 end timestamp.\"),\n groupBy: z\n .enum([\"session\", \"model\", \"day\"])\n .optional()\n .describe(\"How to group results.\"),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n },\n async (args) => toolHandler(\"get-ai-spending\", args),\n );\n\n server.tool(\n \"search_engineering_sessions\",\n \"Search across agent sessions by text. Searches prompts, tool names, and event payloads.\",\n {\n query: z.string().describe(\"Text to search for. Case-insensitive.\"),\n timeRange: z.enum([\"1h\", \"6h\", \"24h\", \"7d\", \"30d\"]).optional(),\n since: z.string().optional(),\n until: z.string().optional(),\n target: z\n .enum([\"claude\", \"codex\", \"gemini\", \"mastra\"])\n .optional()\n .describe(\"Filter by agent type. Omit for all.\"),\n limit: z.number().optional().describe(\"Max results (default 20).\"),\n },\n async (args) => toolHandler(\"search-engineering-sessions\", args),\n );\n\n // ── Config Snapshots ────────────────────────────────────────────────────\n\n server.tool(\n \"list_user_configs\",\n \"List user-level configuration snapshots (CLAUDE.md, settings, etc.) across the org. Shows who has configs and when they were last updated.\",\n {},\n async () => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.listUserConfigSnapshots(orgSlug);\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n\n server.tool(\n \"get_user_config\",\n \"Get detailed user configuration for a specific GitHub user — their CLAUDE.md, settings.json, MCP servers, etc.\",\n {\n githubUsername: z.string().describe(\"GitHub username to look up.\"),\n },\n async (args) => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.getUserConfigDetail(\n orgSlug,\n args.githubUsername,\n );\n if (!result)\n return errorResult(`No config found for ${args.githubUsername}.`);\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n\n server.tool(\n \"list_repo_configs\",\n \"List repository-level configuration snapshots (CLAUDE.md, .mcp.json, etc.) across the org. Optionally filter by repository.\",\n {\n repository: z\n .string()\n .optional()\n .describe(\"Filter by repository full name (e.g. 'org/repo').\"),\n },\n async (args) => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.listRepoConfigSnapshots(\n orgSlug,\n args.repository,\n );\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n\n server.tool(\n \"get_repo_config\",\n \"Get detailed repository configuration — CLAUDE.md files, .mcp.json, and other agent config found in a specific repo.\",\n {\n repository: z\n .string()\n .describe(\"Repository full name (e.g. 'org/repo').\"),\n },\n async (args) => {\n try {\n const orgSlug = getSelectedOrg();\n if (!orgSlug) return errorResult(\"No org selected. Run `fml login`.\");\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n const result = await api.getRepoConfigDetail(orgSlug, args.repository);\n if (!result)\n return errorResult(`No config found for ${args.repository}.`);\n return textResult(result);\n } catch (err) {\n Sentry.captureException(err);\n return errorResult(\n `Error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n },\n );\n}\n"],"mappings":"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","names":["server"]}
@@ -4,7 +4,7 @@ import {
4
4
  readTokens,
5
5
  setSelectedOrg,
6
6
  writeTokens
7
- } from "./chunk-USJO3G5V.js";
7
+ } from "./chunk-TXQRKU2Y.js";
8
8
  import "./chunk-4YEAQJPF.js";
9
9
  import "./chunk-PZ5AY32C.js";
10
10
  export {
@@ -14,4 +14,4 @@ export {
14
14
  setSelectedOrg,
15
15
  writeTokens
16
16
  };
17
- //# sourceMappingURL=token-store-Y35HF3FJ.js.map
17
+ //# sourceMappingURL=token-store-NE7AM2PH.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fml-inc/fml",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "description": "FML agent tools for Claude Code",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/auth/token-store.ts","../src/config.ts","../src/sentry.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { AUTH_STORE_PATH, CONVEX_URL, WORKOS_API_URL } from \"../config.js\";\nimport { FML_DATA_DIR } from \"../dirs.js\";\nimport { Sentry } from \"../sentry.js\";\n\ninterface StoredAuth {\n accessToken: string;\n refreshToken: string;\n expiresAt: number; // Unix timestamp in ms\n user: {\n id: string;\n email: string;\n name: string;\n };\n orgSlug?: string;\n /** WorkOS client ID used to obtain these tokens (needed for refresh) */\n workosClientId?: string;\n /** Token type: \"oauth\" (default) for WorkOS JWT, \"service\" for device flow tokens */\n tokenType?: \"oauth\" | \"service\";\n}\n\nfunction ensureDir(): void {\n const dir = path.dirname(AUTH_STORE_PATH);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n}\n\nexport function readTokens(): StoredAuth | null {\n try {\n const data = fs.readFileSync(AUTH_STORE_PATH, \"utf-8\");\n return JSON.parse(data) as StoredAuth;\n } catch {\n return null;\n }\n}\n\nexport function writeTokens(auth: StoredAuth): void {\n ensureDir();\n fs.writeFileSync(AUTH_STORE_PATH, JSON.stringify(auth, null, 2), {\n mode: 0o600,\n });\n}\n\nexport function getSelectedOrg(): string | null {\n return readTokens()?.orgSlug ?? null;\n}\n\nexport function setSelectedOrg(orgSlug: string): void {\n const stored = readTokens();\n if (!stored) return;\n writeTokens({ ...stored, orgSlug });\n}\n\n// ── Service Token Refresh (sandbox fml_srt_* → fml_st_*) ────────────────────\n\ninterface ServiceTokenCache {\n accessToken: string;\n expiresAt: number;\n}\n\nlet serviceTokenCache: ServiceTokenCache | null = null;\nlet serviceRefreshPromise: Promise<string | null> | null = null;\n\n/** File where the current access token is written for panopticon's tokenCommand */\nconst SERVICE_ACCESS_TOKEN_PATH = path.join(FML_DATA_DIR, \"access_token\");\n\nfunction getSiteUrl(): string {\n const explicit = process.env.CONVEX_SITE_URL;\n if (explicit) return explicit.replace(/\\/$/, \"\");\n return CONVEX_URL.replace(\".convex.cloud\", \".convex.site\");\n}\n\nasync function refreshServiceToken(\n refreshToken: string,\n): Promise<string | null> {\n try {\n const response = await fetch(`${getSiteUrl()}/api/tokens/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${refreshToken}`,\n },\n });\n\n if (!response.ok) {\n console.error(\n `[fml] Service token refresh failed: HTTP ${response.status}`,\n );\n return null;\n }\n\n const data = (await response.json()) as {\n ok: boolean;\n accessToken: string;\n expiresAt: number;\n error?: string;\n };\n\n if (!data.ok || !data.accessToken) {\n console.error(`[fml] Service token refresh error: ${data.error}`);\n return null;\n }\n\n serviceTokenCache = {\n accessToken: data.accessToken,\n expiresAt: data.expiresAt,\n };\n\n // Write to file for panopticon's tokenCommand\n try {\n const dir = path.dirname(SERVICE_ACCESS_TOKEN_PATH);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n fs.writeFileSync(SERVICE_ACCESS_TOKEN_PATH, data.accessToken, {\n mode: 0o600,\n });\n } catch {\n // Non-fatal — in-memory cache still works for the plugin itself\n }\n\n return data.accessToken;\n } catch (err: unknown) {\n Sentry.captureException(err);\n console.error(`[fml] Service token refresh error: ${err}`);\n return null;\n }\n}\n\n// ── Token Resolution ────────────────────────────────────────────────────────\n\n// Mutex to prevent concurrent token refresh races\nlet refreshPromise: Promise<string | null> | null = null;\n\n/**\n * Get a valid access token, refreshing if expired.\n *\n * Three paths:\n * 1. FML_TOKEN=fml_srt_* → exchange refresh token for short-lived access token\n * 2. FML_TOKEN=fml_st_* → use directly (legacy/CI)\n * 3. No FML_TOKEN → OAuth flow (interactive user)\n */\nexport async function getValidToken(): Promise<string | null> {\n const envToken = process.env.FML_TOKEN;\n\n // Service refresh token → exchange for short-lived access token\n if (envToken?.startsWith(\"fml_srt_\")) {\n if (\n serviceTokenCache &&\n serviceTokenCache.expiresAt > Date.now() + 60_000\n ) {\n return serviceTokenCache.accessToken;\n }\n\n if (serviceRefreshPromise) return serviceRefreshPromise;\n serviceRefreshPromise = refreshServiceToken(envToken);\n try {\n return await serviceRefreshPromise;\n } finally {\n serviceRefreshPromise = null;\n }\n }\n\n // Static service token (legacy) or plain env override\n if (envToken) return envToken;\n\n // Stored token path (OAuth or device flow)\n const stored = readTokens();\n if (!stored) {\n console.error(\"[fml] Auth: no stored tokens\");\n return null;\n }\n\n // If token expires in more than 60 seconds, use it\n if (stored.expiresAt > Date.now() + 60_000) {\n return stored.accessToken;\n }\n\n // Device flow tokens: refresh via /api/tokens/refresh (same as sandbox tokens)\n if (stored.tokenType === \"service\") {\n if (serviceRefreshPromise) return serviceRefreshPromise;\n serviceRefreshPromise = (async () => {\n const newAccessToken = await refreshServiceToken(stored.refreshToken);\n if (newAccessToken && serviceTokenCache) {\n writeTokens({\n ...stored,\n accessToken: newAccessToken,\n expiresAt: serviceTokenCache.expiresAt,\n });\n }\n return newAccessToken;\n })();\n try {\n return await serviceRefreshPromise;\n } finally {\n serviceRefreshPromise = null;\n }\n }\n\n // OAuth tokens: refresh via WorkOS\n if (refreshPromise) return refreshPromise;\n\n refreshPromise = refreshToken(stored);\n try {\n return await refreshPromise;\n } finally {\n refreshPromise = null;\n }\n}\n\nasync function refreshToken(stored: StoredAuth): Promise<string | null> {\n if (!stored.workosClientId) {\n console.error(\n \"[fml] Auth: token expired, missing workosClientId — run `fml login`\",\n );\n return null;\n }\n\n try {\n const response = await fetch(\n `${WORKOS_API_URL}/user_management/authenticate`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"refresh_token\",\n client_id: stored.workosClientId,\n refresh_token: stored.refreshToken,\n }),\n },\n );\n\n if (!response.ok) {\n console.error(\n `[fml] Auth: refresh failed (HTTP ${response.status}) — token preserved`,\n );\n return null;\n }\n\n const data = (await response.json()) as {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n user: {\n id: string;\n email: string;\n first_name?: string;\n last_name?: string;\n };\n };\n\n const userName = [data.user.first_name, data.user.last_name]\n .filter(Boolean)\n .join(\" \");\n\n const refreshed: StoredAuth = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Date.now() + (data.expires_in ?? 300) * 1000,\n user: {\n id: data.user.id,\n email: data.user.email,\n name: userName || stored.user.name,\n },\n orgSlug: stored.orgSlug,\n workosClientId: stored.workosClientId,\n };\n\n writeTokens(refreshed);\n return refreshed.accessToken;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(\n `[fml] Auth: refresh failed (network error: ${msg}) — token preserved`,\n );\n Sentry.captureException(err);\n return null;\n }\n}\n","import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { FML_DATA_DIR } from \"./dirs.js\";\n\n// ── Default production deployment ───────────────────────────────────────────\n\nexport const DEFAULT_PROD_URL =\n \"https://trustworthy-chihuahua-382.convex.cloud\";\n\n/** Default sync target name created by `fml install` */\nexport const DEFAULT_TARGET_NAME = \"fml\";\n\n// ── Persistent env selection ────────────────────────────────────────────────\n\ninterface EnvConfig {\n /** Name of the active sync target */\n active: string;\n}\n\nconst ENV_CONFIG_PATH = path.join(FML_DATA_DIR, \"env.json\");\n\nfunction readEnvConfig(): EnvConfig {\n try {\n const raw = fs.readFileSync(ENV_CONFIG_PATH, \"utf-8\");\n return JSON.parse(raw) as EnvConfig;\n } catch {\n return { active: DEFAULT_TARGET_NAME };\n }\n}\n\nexport function writeEnvConfig(config: EnvConfig): void {\n const dir = path.dirname(ENV_CONFIG_PATH);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n fs.writeFileSync(ENV_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\\n`, {\n mode: 0o600,\n });\n}\n\n/**\n * Resolve the active environment by looking up the sync target.\n * Derives the .convex.cloud URL from the target's .convex.site URL.\n * Returns null convexUrl if the target is not found (panopticon not installed).\n */\nexport function getActiveEnv(): { name: string; convexUrl: string | null } {\n const config = readEnvConfig();\n const name = config.active;\n\n try {\n const panoDataDir =\n process.env.PANOPTICON_DATA_DIR ??\n (process.platform === \"darwin\"\n ? path.join(\n os.homedir(),\n \"Library\",\n \"Application Support\",\n \"panopticon\",\n )\n : process.platform === \"win32\"\n ? path.join(\n process.env.APPDATA ??\n path.join(os.homedir(), \"AppData\", \"Roaming\"),\n \"panopticon\",\n )\n : path.join(os.homedir(), \".local\", \"share\", \"panopticon\"));\n const raw = fs.readFileSync(path.join(panoDataDir, \"config.json\"), \"utf-8\");\n const panoConf = JSON.parse(raw) as {\n sync?: { targets?: Array<{ name: string; url: string }> };\n };\n const target = panoConf.sync?.targets?.find((t) => t.name === name);\n if (target) {\n const convexUrl = target.url.replace(\".convex.site\", \".convex.cloud\");\n return { name, convexUrl };\n }\n } catch {\n // Panopticon config not available\n }\n\n return { name, convexUrl: null };\n}\n\n/**\n * Require a resolved Convex URL. Exits with an error if the sync target\n * is not configured (panopticon not installed).\n */\nexport function requireConvexUrl(): string {\n const { name, convexUrl } = getActiveEnv();\n if (!convexUrl) {\n console.error(\n `Sync target \"${name}\" not found. Run \\`fml install\\` or \\`fml sync setup\\`.`,\n );\n process.exit(1);\n }\n return convexUrl;\n}\n\n// ── Exports ─────────────────────────────────────────────────────────────────\n\nconst env = getActiveEnv();\n\n/** Convex deployment URL (switches with `fml env`) */\nexport const CONVEX_URL: string = (\n process.env.FML_CONVEX_URL ??\n env.convexUrl ??\n \"\"\n).replace(/\\/$/, \"\");\n\n/** WorkOS API base URL */\nexport const WORKOS_API_URL = \"https://api.workos.com\";\n\n/** WorkOS authorization base URL */\nexport const WORKOS_AUTH_URL = \"https://auth.fml.inc\";\n\n/** Path to auth token store (per-environment) */\nexport const AUTH_STORE_PATH = path.join(FML_DATA_DIR, `auth.${env.name}.json`);\n\n/** Convex site URL (HTTP actions) — derived from CONVEX_URL */\nexport function getSiteUrl(): string {\n return CONVEX_URL.replace(\".convex.cloud\", \".convex.site\").replace(\n /:\\d+$/,\n \"\",\n );\n}\n","declare const __SENTRY_DSN__: string;\n\nlet initialized = false;\nlet sentry: typeof import(\"@sentry/core\") | null = null;\n\nasync function loadSentry(): Promise<typeof import(\"@sentry/core\") | null> {\n if (sentry) return sentry;\n try {\n sentry = await import(\"@sentry/core\");\n return sentry;\n } catch {\n return null;\n }\n}\n\nexport async function initSentry(): Promise<void> {\n if (initialized) return;\n initialized = true;\n\n const mod = await loadSentry();\n if (!mod) return;\n\n const client = new mod.ServerRuntimeClient({\n dsn: __SENTRY_DSN__,\n tracesSampleRate: 0,\n integrations: [],\n stackParser: mod.createStackParser(mod.nodeStackLineParser()),\n transport: (options) =>\n mod.createTransport(options, async (request) => {\n const res = await fetch(options.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-sentry-envelope\" },\n body: request.body as BodyInit,\n });\n return { statusCode: res.status };\n }),\n });\n\n mod.setCurrentClient(client);\n client.init();\n}\n\nexport const Sentry = {\n captureException: (err: unknown) => sentry?.captureException(err),\n captureMessage: (msg: string) => sentry?.captureMessage(msg),\n} as const;\n"],"mappings":"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","names":["fs","path","path","fs","getSiteUrl","refreshToken"]}