@fml-inc/fml 0.6.0 → 0.7.0

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.
@@ -0,0 +1,21 @@
1
+ import {
2
+ handleFmlStart,
3
+ handleFmlStop,
4
+ handlePanopticonStart,
5
+ handlePanopticonStop,
6
+ handleSyncStart,
7
+ handleSyncStop,
8
+ parsePanopticonRunning
9
+ } from "./chunk-S4TJNLEN.js";
10
+ import "./chunk-YH5ROR7G.js";
11
+ import "./chunk-PZ5AY32C.js";
12
+ export {
13
+ handleFmlStart,
14
+ handleFmlStop,
15
+ handlePanopticonStart,
16
+ handlePanopticonStop,
17
+ handleSyncStart,
18
+ handleSyncStop,
19
+ parsePanopticonRunning
20
+ };
21
+ //# sourceMappingURL=daemon-MSLDHLMH.js.map
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  createFmlClient,
4
4
  getAuthenticatedClient
5
- } from "../chunk-OI562MUP.js";
5
+ } from "../chunk-PTUZMKSN.js";
6
6
  import {
7
7
  FML_DATA_DIR,
8
8
  FML_LOG_DIR,
@@ -12,10 +12,10 @@ import {
12
12
  getValidToken,
13
13
  initSentry,
14
14
  readTokens
15
- } from "../chunk-5BHBYQ2F.js";
15
+ } from "../chunk-VV4JHH4H.js";
16
16
  import {
17
17
  parsePanopticonRunning
18
- } from "../chunk-ZFUJ5ZQP.js";
18
+ } from "../chunk-S4TJNLEN.js";
19
19
  import "../chunk-YH5ROR7G.js";
20
20
  import "../chunk-PZ5AY32C.js";
21
21
 
@@ -598,8 +598,8 @@ async function main() {
598
598
  await server.connect(transport);
599
599
  if (!parsePanopticonRunning()) {
600
600
  try {
601
- const { handleStart } = await import("../daemon-J27JBWLV.js");
602
- await handleStart();
601
+ const { handlePanopticonStart } = await import("../daemon-MSLDHLMH.js");
602
+ await handlePanopticonStart();
603
603
  } catch {
604
604
  console.error(
605
605
  "[fml] Warning: could not start panopticon. Run `fml start` manually"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["#!/usr/bin/env node\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 { authStorePath } 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 const storePath = authStorePath();\n try {\n const files = fs.readdirSync(FML_DATA_DIR).join(\", \") || \"(empty)\";\n console.error(\n `[fml] No auth file at ${storePath}. Dir contents: ${files}`,\n );\n } catch {\n console.error(`[fml] No auth file at ${storePath}. Dir not readable.`);\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 {\n createFmlClient,\n getAuthenticatedClient,\n type ToolResult,\n} from \"../fml-client.js\";\nimport { resolveRepoFromCwd } from \"@fml-inc/panopticon/repo\";\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 createFmlClient(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 produced by `fml_run_analysis_workflow`. USE FOR: finding analyses by topic, type, or status across a repo (or all repos in the org). FILTERS: query (text search), promptKey (exact type like 'deep_security_auditor'), status (complete, running, failed). Pass `repositoryId` to limit the search, or omit to fan out across every repo in the org.\",\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(\n \"Exact filter by analysis type (e.g., 'deep_security_auditor')\",\n ),\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(\n \"Maximum number of results to return total across all matched repos (default 20)\",\n ),\n repositoryId: z\n .string()\n .optional()\n .describe(\n \"Limit to one repository. Omit to search every repo in the caller's organization.\",\n ),\n },\n async (args) => toolHandler(\"search-analysis\", args),\n );\n\n server.tool(\n \"fml_run_analysis_workflow\",\n \"Run comprehensive codebase analysis workflows on a repository. 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. The backend auto-picks the repo for single-repo orgs; pass `repositoryId` explicitly when the org has multiple repos (the tool will otherwise return a clarifying error listing them).\",\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 repositoryId: z\n .string()\n .optional()\n .describe(\n \"Target repository ID. Omit for single-repo orgs (auto-pick). Required when the org has multiple repos — the tool will list them in its error if not provided.\",\n ),\n },\n async (args) => toolHandler(\"run-analysis-workflow\", args),\n );\n\n server.tool(\n \"fml_run_team_analysis\",\n \"Run a team-wide AI coding practice analysis for the caller's organization. Produces a structured report with per-person narratives covering tool usage, collaboration patterns, configuration, and notable changes. Use for prompts like 'run team analysis for the last 14 days' or 'how is the team doing?'. Kicks a durable Convex workflow and returns immediately with a reportId; when called inside a conversation, a completion message is posted back to that conversation when the workflow finishes — wait for that follow-up turn, do not poll. Idempotent: if a run is already in flight, returns the existing reportId with started=false. Requires org-owner access and at least one running dev environment.\",\n {\n windowDays: z\n .number()\n .int()\n .min(1)\n .max(90)\n .optional()\n .describe(\n \"Size of the analysis window in days. Defaults to 30. Clamped to [1, 90]. Match the user's phrasing: 'last 14 days' → 14, 'last month' → 30, 'this week' → 7.\",\n ),\n },\n async (args) => toolHandler(\"run-team-analysis\", 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 // ── Anamnesis ground-truth context ──────────────────────────────────────\n // Factual git/GitHub history for the current repo (which PRs touched a file,\n // what a commit/PR did, etc.), served from the anamnesis corpus.\n\n const resolveRepoArg = (repo?: string): string | null => {\n if (repo) return repo;\n try {\n return resolveRepoFromCwd(process.cwd())?.repo ?? null;\n } catch {\n return null;\n }\n };\n\n const anamnesisResult = (result: ToolResult) =>\n result.ok\n ? textResult(result.result)\n : errorResult(result.error ?? \"Unknown error\");\n\n server.tool(\n \"fml_path_history\",\n \"Ground-truth history for a file path: PRs that touched it and review comments on it (newest first). USE FOR: understanding why a file looks the way it does, who changed it, related PRs.\",\n {\n path: z.string().describe(\"Repo-relative file path, e.g. 'src/app.ts'.\"),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n limit: z.number().optional().describe(\"Max items per list (default 25).\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(\n await api.anamnesisContext(\"path\", {\n repo,\n path: args.path,\n limit: args.limit,\n }),\n );\n },\n );\n\n server.tool(\n \"fml_commit_context\",\n \"Ground-truth context for a commit sha: the PR(s) that contain or merged it, and any revert relationships. Accepts abbreviated shas.\",\n {\n sha: z.string().describe(\"Commit sha (full or abbreviated).\"),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(\n await api.anamnesisContext(\"commit\", { repo, sha: args.sha }),\n );\n },\n );\n\n server.tool(\n \"fml_pr_context\",\n \"Full ground-truth view of a pull request: metadata, commits, changed files, review comments, and derived facts.\",\n {\n number: z.number().describe(\"PR number.\"),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n limit: z.number().optional().describe(\"Max items per list (default 25).\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(\n await api.anamnesisContext(\"pr\", {\n repo,\n number: args.number,\n limit: args.limit,\n }),\n );\n },\n );\n\n server.tool(\n \"fml_anamnesis_query\",\n \"Generic query over the anamnesis fact corpus. Filter by predicate (pr_touches_path, pr_contains_commit, pr_merged_as_commit, review_comment_on_path, commit_reverts_commit, pr_reverts_pr) and/or subject/object.\",\n {\n predicate: z.string().optional().describe(\"Fact predicate to filter by.\"),\n subjectKind: z.string().optional(),\n subjectValue: z.string().optional(),\n objectKind: z.string().optional(),\n objectValue: z.string().optional(),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n limit: z.number().optional().describe(\"Max facts (default 25).\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(await api.anamnesisQuery({ ...args, repo }));\n },\n );\n}\n"],"mappings":"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","names":["server"]}
1
+ {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["#!/usr/bin/env node\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 { authStorePath } 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 const storePath = authStorePath();\n try {\n const files = fs.readdirSync(FML_DATA_DIR).join(\", \") || \"(empty)\";\n console.error(\n `[fml] No auth file at ${storePath}. Dir contents: ${files}`,\n );\n } catch {\n console.error(`[fml] No auth file at ${storePath}. Dir not readable.`);\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 { handlePanopticonStart } = await import(\"../commands/daemon.js\");\n await handlePanopticonStart();\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 {\n createFmlClient,\n getAuthenticatedClient,\n type ToolResult,\n} from \"../fml-client.js\";\nimport { resolveRepoFromCwd } from \"@fml-inc/panopticon/repo\";\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 createFmlClient(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 produced by `fml_run_analysis_workflow`. USE FOR: finding analyses by topic, type, or status across a repo (or all repos in the org). FILTERS: query (text search), promptKey (exact type like 'deep_security_auditor'), status (complete, running, failed). Pass `repositoryId` to limit the search, or omit to fan out across every repo in the org.\",\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(\n \"Exact filter by analysis type (e.g., 'deep_security_auditor')\",\n ),\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(\n \"Maximum number of results to return total across all matched repos (default 20)\",\n ),\n repositoryId: z\n .string()\n .optional()\n .describe(\n \"Limit to one repository. Omit to search every repo in the caller's organization.\",\n ),\n },\n async (args) => toolHandler(\"search-analysis\", args),\n );\n\n server.tool(\n \"fml_run_analysis_workflow\",\n \"Run comprehensive codebase analysis workflows on a repository. 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. The backend auto-picks the repo for single-repo orgs; pass `repositoryId` explicitly when the org has multiple repos (the tool will otherwise return a clarifying error listing them).\",\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 repositoryId: z\n .string()\n .optional()\n .describe(\n \"Target repository ID. Omit for single-repo orgs (auto-pick). Required when the org has multiple repos — the tool will list them in its error if not provided.\",\n ),\n },\n async (args) => toolHandler(\"run-analysis-workflow\", args),\n );\n\n server.tool(\n \"fml_run_team_analysis\",\n \"Run a team-wide AI coding practice analysis for the caller's organization. Produces a structured report with per-person narratives covering tool usage, collaboration patterns, configuration, and notable changes. Use for prompts like 'run team analysis for the last 14 days' or 'how is the team doing?'. Kicks a durable Convex workflow and returns immediately with a reportId; when called inside a conversation, a completion message is posted back to that conversation when the workflow finishes — wait for that follow-up turn, do not poll. Idempotent: if a run is already in flight, returns the existing reportId with started=false. Requires org-owner access and at least one running dev environment.\",\n {\n windowDays: z\n .number()\n .int()\n .min(1)\n .max(90)\n .optional()\n .describe(\n \"Size of the analysis window in days. Defaults to 30. Clamped to [1, 90]. Match the user's phrasing: 'last 14 days' → 14, 'last month' → 30, 'this week' → 7.\",\n ),\n },\n async (args) => toolHandler(\"run-team-analysis\", 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 // ── Anamnesis ground-truth context ──────────────────────────────────────\n // Factual git/GitHub history for the current repo (which PRs touched a file,\n // what a commit/PR did, etc.), served from the anamnesis corpus.\n\n const resolveRepoArg = (repo?: string): string | null => {\n if (repo) return repo;\n try {\n return resolveRepoFromCwd(process.cwd())?.repo ?? null;\n } catch {\n return null;\n }\n };\n\n const anamnesisResult = (result: ToolResult) =>\n result.ok\n ? textResult(result.result)\n : errorResult(result.error ?? \"Unknown error\");\n\n server.tool(\n \"fml_path_history\",\n \"Ground-truth history for a file path: PRs that touched it and review comments on it (newest first). USE FOR: understanding why a file looks the way it does, who changed it, related PRs.\",\n {\n path: z.string().describe(\"Repo-relative file path, e.g. 'src/app.ts'.\"),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n limit: z.number().optional().describe(\"Max items per list (default 25).\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(\n await api.anamnesisContext(\"path\", {\n repo,\n path: args.path,\n limit: args.limit,\n }),\n );\n },\n );\n\n server.tool(\n \"fml_commit_context\",\n \"Ground-truth context for a commit sha: the PR(s) that contain or merged it, and any revert relationships. Accepts abbreviated shas.\",\n {\n sha: z.string().describe(\"Commit sha (full or abbreviated).\"),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(\n await api.anamnesisContext(\"commit\", { repo, sha: args.sha }),\n );\n },\n );\n\n server.tool(\n \"fml_pr_context\",\n \"Full ground-truth view of a pull request: metadata, commits, changed files, review comments, and derived facts.\",\n {\n number: z.number().describe(\"PR number.\"),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n limit: z.number().optional().describe(\"Max items per list (default 25).\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(\n await api.anamnesisContext(\"pr\", {\n repo,\n number: args.number,\n limit: args.limit,\n }),\n );\n },\n );\n\n server.tool(\n \"fml_anamnesis_query\",\n \"Generic query over the anamnesis fact corpus. Filter by predicate (pr_touches_path, pr_contains_commit, pr_merged_as_commit, review_comment_on_path, commit_reverts_commit, pr_reverts_pr) and/or subject/object.\",\n {\n predicate: z.string().optional().describe(\"Fact predicate to filter by.\"),\n subjectKind: z.string().optional(),\n subjectValue: z.string().optional(),\n objectKind: z.string().optional(),\n objectValue: z.string().optional(),\n repo: z\n .string()\n .optional()\n .describe(\"owner/repo. Defaults to the current working repo.\"),\n limit: z.number().optional().describe(\"Max facts (default 25).\"),\n },\n async (args) => {\n const repo = resolveRepoArg(args.repo);\n if (!repo)\n return errorResult(\n \"Could not determine repo — pass `repo` (owner/repo).\",\n );\n const api = await getAuthenticatedClient();\n if (!api) return errorResult(\"Not authenticated. Run `fml login`.\");\n return anamnesisResult(await api.anamnesisQuery({ ...args, repo }));\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAEA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACJrC,SAAS,SAAS;AAWlB,SAAS,0BAA0B;AAKnC,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;AAAA,QACC;AAAA,MACF;AAAA,MACF,QAAQ,EACL,KAAK,CAAC,YAAY,WAAW,WAAW,QAAQ,CAAC,EACjD,SAAS,EACT,SAAS,kBAAkB;AAAA,MAC9B,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,MACF,cAAc,EACX,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ;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,MACF,cAAc,EACX,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ;AAAA,IACA,OAAO,SAAS,YAAY,yBAAyB,IAAI;AAAA,EAC3D;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAY,EACT,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,EAAE,EACN,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,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,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;AAMA,QAAM,iBAAiB,CAAC,SAAiC;AACvD,QAAI,KAAM,QAAO;AACjB,QAAI;AACF,aAAO,mBAAmB,QAAQ,IAAI,CAAC,GAAG,QAAQ;AAAA,IACpD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,WACvB,OAAO,KACH,WAAW,OAAO,MAAM,IACxB,YAAY,OAAO,SAAS,eAAe;AAEjD,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,SAAS,6CAA6C;AAAA,MACvE,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mDAAmD;AAAA,MAC/D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,IAC1E;AAAA,IACA,OAAO,SAAS;AACd,YAAM,OAAO,eAAe,KAAK,IAAI;AACrC,UAAI,CAAC;AACH,eAAO;AAAA,UACL;AAAA,QACF;AACF,YAAM,MAAM,MAAM,uBAAuB;AACzC,UAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,aAAO;AAAA,QACL,MAAM,IAAI,iBAAiB,QAAQ;AAAA,UACjC;AAAA,UACA,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,KAAK,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,MAC5D,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mDAAmD;AAAA,IACjE;AAAA,IACA,OAAO,SAAS;AACd,YAAM,OAAO,eAAe,KAAK,IAAI;AACrC,UAAI,CAAC;AACH,eAAO;AAAA,UACL;AAAA,QACF;AACF,YAAM,MAAM,MAAM,uBAAuB;AACzC,UAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,aAAO;AAAA,QACL,MAAM,IAAI,iBAAiB,UAAU,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,OAAO,EAAE,SAAS,YAAY;AAAA,MACxC,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mDAAmD;AAAA,MAC/D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,IAC1E;AAAA,IACA,OAAO,SAAS;AACd,YAAM,OAAO,eAAe,KAAK,IAAI;AACrC,UAAI,CAAC;AACH,eAAO;AAAA,UACL;AAAA,QACF;AACF,YAAM,MAAM,MAAM,uBAAuB;AACzC,UAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,aAAO;AAAA,QACL,MAAM,IAAI,iBAAiB,MAAM;AAAA,UAC/B;AAAA,UACA,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,MACxE,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,MACjC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,MAClC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,MAChC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,MACjC,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mDAAmD;AAAA,MAC/D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,IACjE;AAAA,IACA,OAAO,SAAS;AACd,YAAM,OAAO,eAAe,KAAK,IAAI;AACrC,UAAI,CAAC;AACH,eAAO;AAAA,UACL;AAAA,QACF;AACF,YAAM,MAAM,MAAM,uBAAuB;AACzC,UAAI,CAAC,IAAK,QAAO,YAAY,qCAAqC;AAClE,aAAO,gBAAgB,MAAM,IAAI,eAAe,EAAE,GAAG,MAAM,KAAK,CAAC,CAAC;AAAA,IACpE;AAAA,EACF;AACF;;;ADnvBA,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,UAAM,YAAY,cAAc;AAChC,QAAI;AACF,YAAM,QAAQ,GAAG,YAAY,YAAY,EAAE,KAAK,IAAI,KAAK;AACzD,cAAQ;AAAA,QACN,yBAAyB,SAAS,mBAAmB,KAAK;AAAA,MAC5D;AAAA,IACF,QAAQ;AACN,cAAQ,MAAM,yBAAyB,SAAS,qBAAqB;AAAA,IACvE;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,sBAAsB,IAAI,MAAM,OAAO,uBAAuB;AACtE,YAAM,sBAAsB;AAAA,IAC9B,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,16 +1,20 @@
1
1
  import {
2
+ SERVICE_TOKEN_LOGIN_USER_ID,
2
3
  getSelectedOrg,
3
4
  getValidToken,
4
5
  readTokens,
5
6
  setSelectedOrg,
7
+ storeServiceRefreshToken,
6
8
  writeTokens
7
- } from "./chunk-5BHBYQ2F.js";
9
+ } from "./chunk-VV4JHH4H.js";
8
10
  import "./chunk-PZ5AY32C.js";
9
11
  export {
12
+ SERVICE_TOKEN_LOGIN_USER_ID,
10
13
  getSelectedOrg,
11
14
  getValidToken,
12
15
  readTokens,
13
16
  setSelectedOrg,
17
+ storeServiceRefreshToken,
14
18
  writeTokens
15
19
  };
16
- //# sourceMappingURL=token-store-T2RNLXRM.js.map
20
+ //# sourceMappingURL=token-store-3IQSLOTQ.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fml-inc/fml",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
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/dirs.ts","../src/sentry.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n CONVEX_URL,\n WORKOS_API_URL,\n authStorePath,\n authStorePathFor,\n resolveEnvConvexUrl,\n} 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 storePathFor(envName?: string): string {\n return envName ? authStorePathFor(envName) : authStorePath();\n}\n\nfunction ensureDir(envName?: string): void {\n const dir = path.dirname(storePathFor(envName));\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n}\n\nexport function readTokens(envName?: string): StoredAuth | null {\n try {\n const data = fs.readFileSync(storePathFor(envName), \"utf-8\");\n return JSON.parse(data) as StoredAuth;\n } catch {\n return null;\n }\n}\n\nexport function writeTokens(auth: StoredAuth, envName?: string): void {\n ensureDir(envName);\n fs.writeFileSync(storePathFor(envName), 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\n/**\n * Caches and refresh-deduplication promises are keyed by env so in-process\n * callers that read multiple envs (`getValidToken({ env: \"a\" })` followed by\n * `getValidToken({ env: \"b\" })`) don't cross-contaminate. The sentinel\n * \"__active__\" stands in for calls with no explicit env.\n */\nconst ACTIVE_KEY = \"__active__\";\nconst serviceTokenCaches = new Map<string, ServiceTokenCache>();\nconst serviceRefreshPromises = new Map<string, Promise<string | null>>();\nconst oauthRefreshPromises = new Map<string, Promise<string | null>>();\nconst cacheKey = (envName?: string): string => envName ?? ACTIVE_KEY;\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(envName?: string): string {\n const explicit = process.env.CONVEX_SITE_URL;\n if (explicit) return explicit.replace(/\\/$/, \"\");\n if (envName) {\n const envConvexUrl = resolveEnvConvexUrl(envName);\n if (envConvexUrl) {\n return envConvexUrl.replace(\".convex.cloud\", \".convex.site\");\n }\n }\n return CONVEX_URL.replace(\".convex.cloud\", \".convex.site\");\n}\n\nasync function refreshServiceToken(\n refreshToken: string,\n envName?: string,\n): Promise<string | null> {\n try {\n const response = await fetch(`${getSiteUrl(envName)}/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 serviceTokenCaches.set(cacheKey(envName), {\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/**\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(opts?: {\n env?: string;\n}): Promise<string | null> {\n const envName = opts?.env;\n const key = cacheKey(envName);\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 const cached = serviceTokenCaches.get(key);\n if (cached && cached.expiresAt > Date.now() + 60_000) {\n return cached.accessToken;\n }\n\n const inflight = serviceRefreshPromises.get(key);\n if (inflight) return inflight;\n const promise = refreshServiceToken(envToken, envName);\n serviceRefreshPromises.set(key, promise);\n try {\n return await promise;\n } finally {\n serviceRefreshPromises.delete(key);\n }\n }\n\n // Static service access token.\n if (envToken?.startsWith(\"fml_st_\")) return envToken;\n\n if (envToken) {\n console.error(\n \"[fml] Auth: unsupported FML_TOKEN format. Expected fml_srt_* or fml_st_*.\",\n );\n return null;\n }\n\n // Stored token path (OAuth or device flow)\n const stored = readTokens(envName);\n if (!stored) {\n console.error(\n envName\n ? `[fml] Auth: no stored tokens for env \"${envName}\"`\n : \"[fml] Auth: no stored tokens\",\n );\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 const inflight = serviceRefreshPromises.get(key);\n if (inflight) return inflight;\n const promise = (async () => {\n const newAccessToken = await refreshServiceToken(\n stored.refreshToken,\n envName,\n );\n const cached = serviceTokenCaches.get(key);\n if (newAccessToken && cached) {\n writeTokens(\n {\n ...stored,\n accessToken: newAccessToken,\n expiresAt: cached.expiresAt,\n },\n envName,\n );\n }\n return newAccessToken;\n })();\n serviceRefreshPromises.set(key, promise);\n try {\n return await promise;\n } finally {\n serviceRefreshPromises.delete(key);\n }\n }\n\n // OAuth tokens: refresh via WorkOS\n const inflight = oauthRefreshPromises.get(key);\n if (inflight) return inflight;\n const promise = refreshToken(stored, envName);\n oauthRefreshPromises.set(key, promise);\n try {\n return await promise;\n } finally {\n oauthRefreshPromises.delete(key);\n }\n}\n\nasync function refreshToken(\n stored: StoredAuth,\n envName?: string,\n): 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, envName);\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/** Convex site URL for sync/HTTP actions — derived from DEFAULT_PROD_URL. */\nexport const DEFAULT_SYNC_URL = DEFAULT_PROD_URL.replace(\n \".convex.cloud\",\n \".convex.site\",\n);\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/** Whether an env selection has already been persisted (not a fresh install). */\nexport function envConfigExists(): boolean {\n return fs.existsSync(ENV_CONFIG_PATH);\n}\n\n/**\n * Look up a panopticon sync target by name and return its Convex deployment\n * URL (`.convex.cloud`). Returns null if panopticon isn't installed or the\n * target isn't configured.\n */\nexport function resolveEnvConvexUrl(envName: string): string | null {\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 === envName);\n if (target) {\n return target.url.replace(\".convex.site\", \".convex.cloud\");\n }\n } catch {\n // Panopticon config not available\n }\n return null;\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 name = readEnvConfig().active;\n return { name, convexUrl: resolveEnvConvexUrl(name) };\n}\n\n/** Auth store path for a specific env name (tokens are segregated per env). */\nexport function authStorePathFor(envName: string): string {\n return path.join(FML_DATA_DIR, `auth.${envName}.json`);\n}\n\n/**\n * Env names flow into `tokenCommand` strings that panopticon shells out, so\n * refuse anything that could carry shell metacharacters. Target names are\n * also written to panopticon's config by this rule (see `fml env use`).\n */\nconst ENV_NAME_RE = /^[A-Za-z0-9_-]+$/;\nexport function isValidEnvName(envName: string): boolean {\n return ENV_NAME_RE.test(envName);\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\n/**\n * Convex deployment URL (switches with `fml env`).\n *\n * Resolution order:\n * 1. `FML_CONVEX_URL` env var (dev/preview overrides)\n * 2. Active panopticon sync target\n * 3. `DEFAULT_PROD_URL` — lets a fresh `npm install -g` log in against\n * prod before `fml install` has had a chance to seed a sync target.\n */\nexport const CONVEX_URL: string = (\n process.env.FML_CONVEX_URL ??\n getActiveEnv().convexUrl ??\n DEFAULT_PROD_URL\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/**\n * Path to the auth token store for the *currently* active env. Resolved\n * lazily so long-running processes that outlive an `fml env use` switch\n * still read the correct file on the next call.\n */\nexport function authStorePath(): string {\n return authStorePathFor(getActiveEnv().name);\n}\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","/**\n * Platform-native directories for FML runtime data.\n *\n * macOS: ~/Library/Application Support/fml + ~/Library/Logs/fml\n * Linux: ~/.local/share/fml + ~/.local/state/fml/log\n * Windows: %APPDATA%/fml + %APPDATA%/fml/logs\n */\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst home = os.homedir();\n\nfunction dataDir(): string {\n // Override (tests, eval replays, sandboxes) so they never touch a real\n // user's auth/config in the platform-default location.\n if (process.env.FML_DATA_DIR) return process.env.FML_DATA_DIR;\n switch (process.platform) {\n case \"darwin\":\n return path.join(home, \"Library\", \"Application Support\", \"fml\");\n case \"win32\":\n return path.join(\n process.env.APPDATA ?? path.join(home, \"AppData\", \"Roaming\"),\n \"fml\",\n );\n default:\n return path.join(\n process.env.XDG_DATA_HOME ?? path.join(home, \".local\", \"share\"),\n \"fml\",\n );\n }\n}\n\nfunction logDir(): string {\n if (process.env.FML_LOG_DIR) return process.env.FML_LOG_DIR;\n switch (process.platform) {\n case \"darwin\":\n return path.join(home, \"Library\", \"Logs\", \"fml\");\n case \"win32\":\n return path.join(\n process.env.APPDATA ?? path.join(home, \"AppData\", \"Roaming\"),\n \"fml\",\n \"logs\",\n );\n default:\n return path.join(\n process.env.XDG_STATE_HOME ?? path.join(home, \".local\", \"state\"),\n \"fml\",\n \"log\",\n );\n }\n}\n\n/** Runtime data — auth tokens, env config, PID files */\nexport const FML_DATA_DIR = dataDir();\n\n/** Log files — daemon logs, MCP logs */\nexport const FML_LOG_DIR = logDir();\n\n/** Ensure both directories exist (called once at startup). */\nexport function ensureDirs(): void {\n fs.mkdirSync(FML_DATA_DIR, { recursive: true, mode: 0o700 });\n fs.mkdirSync(FML_LOG_DIR, { recursive: true, mode: 0o700 });\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","fs","os","path","path","fs","os","path","fs","getSiteUrl","refreshToken","inflight","promise"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/fml-client.ts"],"sourcesContent":["import { resolveRepoFromCwd } from \"@fml-inc/panopticon/repo\";\nimport { getSelectedOrg, getValidToken } from \"./auth/token-store.js\";\nimport { CONVEX_URL } from \"./config.js\";\nimport type {\n RepoConfigSnapshotDetail,\n RepoConfigSnapshotSummary,\n ResolvedRepo,\n UserConfigSnapshotDetail,\n UserConfigSnapshotSummary,\n} from \"./types.js\";\n\n// ── Shared plumbing ─────────────────────────────────────────────────────────\n\nexport type ToolCategory =\n | \"messages\"\n | \"slack\"\n | \"skills\"\n | \"analysis\"\n | \"integrations\"\n | \"engineering\"\n | \"memory\"\n | \"automations\"\n | \"amplitude\"\n | \"posthog\"\n | \"meta-ads\";\n\nexport interface PublicToolDescriptor {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n category?: ToolCategory;\n experimental?: boolean;\n}\n\nexport type KnownToolErrorCode =\n | \"UNAUTHENTICATED\"\n | \"TOKEN_EXPIRED\"\n | \"ACCESS_DENIED\"\n | \"ORG_REQUIRED\"\n | \"ORG_NOT_FOUND\"\n | \"REPO_NOT_FOUND\"\n | \"UNKNOWN_TOOL\"\n | \"INVALID_ARGS\"\n | \"RATE_LIMITED\"\n | \"INTERNAL_ERROR\";\n\nexport type ToolErrorCode = KnownToolErrorCode | (string & {});\n\nexport interface ToolResult {\n ok: boolean;\n result?: unknown;\n error?: string;\n code?: ToolErrorCode;\n}\n\nexport interface OrgInfo {\n _id: string;\n name: string;\n slug?: string;\n repos?: Array<{\n _id: string;\n fullName: string;\n owner: string;\n name: string;\n private: boolean;\n }>;\n}\n\nconst LOGIN_EXPIRED_MESSAGE =\n \"Authentication expired. Run `fml login` to sign in again, then restart Claude Code.\";\n\nfunction isAuthError(data: { error?: string; code?: string }): boolean {\n return (\n data.code === \"UNAUTHENTICATED\" ||\n data.code === \"TOKEN_EXPIRED\" ||\n data.error?.includes(\"Unauthorized\") === true ||\n data.error?.includes(\"not authenticated\") === true\n );\n}\n\nfunction normalizeToolResult(data: ToolResult): ToolResult {\n if (!data.ok && isAuthError(data)) {\n return { ok: false, error: LOGIN_EXPIRED_MESSAGE, code: data.code };\n }\n return data;\n}\n\nfunction requireToolResult<T>(result: ToolResult): T {\n if (!result.ok) {\n throw new Error(result.error ?? \"Backend request failed\");\n }\n return result.result as T;\n}\n\n// ── API client factory ──────────────────────────────────────────────────────\n\nexport function createFmlClient(token: string) {\n const isServiceToken = token.startsWith(\"fml_st_\");\n\n /**\n * Derive the Convex site URL (HTTP actions) from the cloud URL.\n * Convex uses paired domains: *.convex.cloud for client APIs,\n * *.convex.site for HTTP actions. This is a stable Convex convention.\n */\n function getSiteUrl(): string {\n return CONVEX_URL.replace(\".convex.cloud\", \".convex.site\").replace(\n /\\/$/,\n \"\",\n );\n }\n\n /** Authed fetch to a fml-be HTTP action, parsing the JSON envelope. */\n async function fetchContext(\n pathAndQuery: string,\n init: RequestInit,\n ): Promise<ToolResult> {\n try {\n const res = await fetch(`${getSiteUrl()}${pathAndQuery}`, {\n ...init,\n headers: {\n Authorization: `Bearer ${token}`,\n ...(init.body ? { \"Content-Type\": \"application/json\" } : {}),\n },\n });\n const text = await res.text();\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch {\n return { ok: false, error: `HTTP ${res.status}: ${text.slice(0, 200)}` };\n }\n if (!res.ok) {\n const envelope = data as { error?: string; code?: ToolErrorCode };\n return normalizeToolResult({\n ok: false,\n error: envelope.error ?? `HTTP ${res.status}`,\n code: envelope.code,\n });\n }\n return { ok: true, result: data };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n }\n\n return {\n // ── Orgs ──────────────────────────────────────────────────────────────\n\n async queryOrgs(): Promise<OrgInfo[]> {\n const result = await this.callBackend(\"list-orgs\", {});\n return requireToolResult<OrgInfo[] | null>(result) ?? [];\n },\n\n // ── Tool gateway ─────────────────────────────────────────────────────\n\n async callBackend(\n toolName: string,\n args: Record<string, unknown>,\n opts?: { org?: string },\n ): Promise<ToolResult> {\n try {\n // Unified CLI/MCP path: POST to the dual-auth HTTP endpoint for both\n // service tokens and WorkOS/JWT user tokens. This keeps command\n // handlers independent of token class and matches the backend's agent\n // tool contract.\n const body: Record<string, unknown> = { toolName, args };\n\n // Explicit org > stored org selection > repo-based inference.\n const org = opts?.org ?? getSelectedOrg();\n if (org) body.org = org;\n const repo = resolveRepoFromCwd(process.cwd());\n if (repo) body.repo = repo.repo;\n\n // Thread user identity for service-token-backed sandbox agents. For\n // JWT callers this is ignored by the backend; for service tokens the\n // backend validates membership before honoring the override.\n if (isServiceToken) {\n const { readTokens } = await import(\"./auth/token-store.js\");\n const userExternalId =\n process.env.FML_USER_EXTERNAL_ID ?? readTokens()?.user.id;\n if (userExternalId) body.userExternalId = userExternalId;\n }\n\n const res = await fetch(`${getSiteUrl()}/api/tools/execute`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify(body),\n });\n const text = await res.text();\n let data: ToolResult;\n try {\n data = JSON.parse(text) as ToolResult;\n } catch {\n return {\n ok: false,\n error: `HTTP ${res.status}: ${text.slice(0, 200)}`,\n };\n }\n if (!res.ok && !data.error) {\n return { ok: false, error: `HTTP ${res.status}` };\n }\n return normalizeToolResult(data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(\"Unauthorized\") || msg.includes(\"not authenticated\")) {\n return { ok: false, error: LOGIN_EXPIRED_MESSAGE };\n }\n return { ok: false, error: msg };\n }\n },\n\n // ── Tool catalog ─────────────────────────────────────────────────────\n\n async listTools(pluginVersion?: string): Promise<PublicToolDescriptor[]> {\n try {\n // Always use the HTTP catalog endpoint — it accepts both a service\n // token and a user WorkOS JWT. A raw client.query rejects the user\n // JWT (no `aud` claim, which Convex's client-protocol auth requires),\n // so the httpAction path is the only one that works for both.\n const url = new URL(`${getSiteUrl()}/api/tools/list`);\n if (pluginVersion) url.searchParams.set(\"pluginVersion\", pluginVersion);\n const res = await fetch(url.toString(), {\n headers: { Authorization: `Bearer ${token}` },\n });\n const text = await res.text();\n let data: {\n ok: boolean;\n descriptors?: PublicToolDescriptor[];\n error?: string;\n code?: ToolErrorCode;\n };\n try {\n data = JSON.parse(text) as typeof data;\n } catch {\n throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);\n }\n if (!data.ok || !data.descriptors) {\n if (isAuthError(data)) {\n throw new Error(LOGIN_EXPIRED_MESSAGE);\n }\n throw new Error(data.error ?? `HTTP ${res.status}`);\n }\n return data.descriptors;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(\"Unauthorized\") || msg.includes(\"not authenticated\")) {\n throw new Error(LOGIN_EXPIRED_MESSAGE);\n }\n throw err;\n }\n },\n\n // ── Anamnesis ground-truth context ───────────────────────────────────\n\n /**\n * Call a read-only anamnesis context endpoint\n * (/v1/anamnesis/context/{path,commit,pr}). Sends the current token\n * (service token or user JWT) — the endpoint accepts either.\n */\n async anamnesisContext(\n kind: \"path\" | \"commit\" | \"pr\",\n params: Record<string, string | number | undefined>,\n ): Promise<ToolResult> {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v != null) qs.set(k, String(v));\n }\n return fetchContext(`/v1/anamnesis/context/${kind}?${qs.toString()}`, {\n method: \"GET\",\n });\n },\n\n /** POST /v1/anamnesis/context/query — generic predicate query. */\n async anamnesisQuery(body: Record<string, unknown>): Promise<ToolResult> {\n return fetchContext(\"/v1/anamnesis/context/query\", {\n method: \"POST\",\n body: JSON.stringify(body),\n });\n },\n\n // ── Repo resolution ──────────────────────────────────────────────────\n\n async resolveRepo(\n orgSlug: string,\n repoFullName: string,\n ): Promise<ResolvedRepo | null> {\n const result = await this.callBackend(\n \"resolve-repo\",\n { orgSlug, repoFullName },\n { org: orgSlug },\n );\n return requireToolResult<ResolvedRepo | null>(result);\n },\n\n // ── Config snapshots ─────────────────────────────────────────────────\n\n async listUserConfigSnapshots(\n orgSlug: string,\n ): Promise<UserConfigSnapshotSummary[]> {\n const result = await this.callBackend(\n \"list-user-config-snapshots\",\n { orgSlug },\n { org: orgSlug },\n );\n return requireToolResult<UserConfigSnapshotSummary[] | null>(result) ?? [];\n },\n\n async getUserConfigDetail(\n orgSlug: string,\n githubUsername: string,\n ): Promise<UserConfigSnapshotDetail | null> {\n const result = await this.callBackend(\n \"get-user-config-snapshot\",\n { orgSlug, githubUsername },\n { org: orgSlug },\n );\n return requireToolResult<UserConfigSnapshotDetail | null>(result);\n },\n\n async listRepoConfigSnapshots(\n orgSlug: string,\n repository?: string,\n ): Promise<RepoConfigSnapshotSummary[]> {\n const result = await this.callBackend(\n \"list-repo-config-snapshots\",\n { orgSlug, repository },\n { org: orgSlug },\n );\n return requireToolResult<RepoConfigSnapshotSummary[] | null>(result) ?? [];\n },\n\n async getRepoConfigDetail(\n orgSlug: string,\n repository: string,\n ): Promise<RepoConfigSnapshotDetail | null> {\n const result = await this.callBackend(\n \"get-repo-config-snapshot\",\n { orgSlug, repository },\n { org: orgSlug },\n );\n return requireToolResult<RepoConfigSnapshotDetail | null>(result);\n },\n };\n}\n\n// ── Convenience: auto-authenticated client ──────────────────────────────────\n\n/**\n * Create an API client using the stored auth token.\n * Returns null if not authenticated.\n */\nexport async function getAuthenticatedClient() {\n const token = await getValidToken();\n if (!token) return null;\n return createFmlClient(token);\n}\n"],"mappings":"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","names":[]}
@@ -1,41 +0,0 @@
1
- import {
2
- panopticonExec
3
- } from "./chunk-YH5ROR7G.js";
4
-
5
- // src/commands/daemon.ts
6
- function parsePanopticonRunning() {
7
- const result = panopticonExec("status");
8
- const serverLine = result.stdout.split("\n").find((l) => l.startsWith("Server:"));
9
- return result.ok && /running/i.test(serverLine ?? "");
10
- }
11
- async function handleStart() {
12
- const startResult = panopticonExec("start");
13
- console.log(
14
- startResult.stdout.trim() || (startResult.ok ? "Panopticon started" : "Failed to start panopticon")
15
- );
16
- }
17
- function handleStop() {
18
- const stopResult = panopticonExec("stop");
19
- console.log(stopResult.stdout.trim() || "Panopticon stopped");
20
- }
21
- function handleSyncStart() {
22
- const result = panopticonExec("sync", "enable");
23
- console.log(
24
- result.stdout.trim() || (result.ok ? "Sync enabled" : "Failed to enable sync")
25
- );
26
- }
27
- function handleSyncStop() {
28
- const result = panopticonExec("sync", "disable");
29
- console.log(
30
- result.stdout.trim() || (result.ok ? "Sync disabled" : "Failed to disable sync")
31
- );
32
- }
33
-
34
- export {
35
- parsePanopticonRunning,
36
- handleStart,
37
- handleStop,
38
- handleSyncStart,
39
- handleSyncStop
40
- };
41
- //# sourceMappingURL=chunk-ZFUJ5ZQP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/daemon.ts"],"sourcesContent":["import { panopticonExec } from \"../daemon-utils.js\";\n\n// ── Status helpers (used by doctor, status) ─────────────────────────────────\n\nexport function parsePanopticonRunning(): boolean {\n const result = panopticonExec(\"status\");\n const serverLine = result.stdout\n .split(\"\\n\")\n .find((l) => l.startsWith(\"Server:\"));\n return result.ok && /running/i.test(serverLine ?? \"\");\n}\n\n// ── Panopticon server start / stop (used by CLI, env restart, install, MCP) ──\n\nexport async function handleStart(): Promise<void> {\n const startResult = panopticonExec(\"start\");\n console.log(\n startResult.stdout.trim() ||\n (startResult.ok ? \"Panopticon started\" : \"Failed to start panopticon\"),\n );\n}\n\nexport function handleStop(): void {\n const stopResult = panopticonExec(\"stop\");\n console.log(stopResult.stdout.trim() || \"Panopticon stopped\");\n}\n\n// ── Sync start / stop (used by `fml sync start|stop`) ───────────────────────\n// Sync runs inside the panopticon server; these toggle the persisted\n// sync-enabled flag via panopticon, independent of the server lifecycle.\n\nexport function handleSyncStart(): void {\n const result = panopticonExec(\"sync\", \"enable\");\n console.log(\n result.stdout.trim() ||\n (result.ok ? \"Sync enabled\" : \"Failed to enable sync\"),\n );\n}\n\nexport function handleSyncStop(): void {\n const result = panopticonExec(\"sync\", \"disable\");\n console.log(\n result.stdout.trim() ||\n (result.ok ? \"Sync disabled\" : \"Failed to disable sync\"),\n );\n}\n"],"mappings":";;;;;AAIO,SAAS,yBAAkC;AAChD,QAAM,SAAS,eAAe,QAAQ;AACtC,QAAM,aAAa,OAAO,OACvB,MAAM,IAAI,EACV,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC;AACtC,SAAO,OAAO,MAAM,WAAW,KAAK,cAAc,EAAE;AACtD;AAIA,eAAsB,cAA6B;AACjD,QAAM,cAAc,eAAe,OAAO;AAC1C,UAAQ;AAAA,IACN,YAAY,OAAO,KAAK,MACrB,YAAY,KAAK,uBAAuB;AAAA,EAC7C;AACF;AAEO,SAAS,aAAmB;AACjC,QAAM,aAAa,eAAe,MAAM;AACxC,UAAQ,IAAI,WAAW,OAAO,KAAK,KAAK,oBAAoB;AAC9D;AAMO,SAAS,kBAAwB;AACtC,QAAM,SAAS,eAAe,QAAQ,QAAQ;AAC9C,UAAQ;AAAA,IACN,OAAO,OAAO,KAAK,MAChB,OAAO,KAAK,iBAAiB;AAAA,EAClC;AACF;AAEO,SAAS,iBAAuB;AACrC,QAAM,SAAS,eAAe,QAAQ,SAAS;AAC/C,UAAQ;AAAA,IACN,OAAO,OAAO,KAAK,MAChB,OAAO,KAAK,kBAAkB;AAAA,EACnC;AACF;","names":[]}
@@ -1,17 +0,0 @@
1
- import {
2
- handleStart,
3
- handleStop,
4
- handleSyncStart,
5
- handleSyncStop,
6
- parsePanopticonRunning
7
- } from "./chunk-ZFUJ5ZQP.js";
8
- import "./chunk-YH5ROR7G.js";
9
- import "./chunk-PZ5AY32C.js";
10
- export {
11
- handleStart,
12
- handleStop,
13
- handleSyncStart,
14
- handleSyncStop,
15
- parsePanopticonRunning
16
- };
17
- //# sourceMappingURL=daemon-J27JBWLV.js.map