@levelup-log/mcp-server 0.2.0 → 0.3.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.
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@levelup-log/mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP Server that turns your daily tasks into game-like achievements",
5
5
  "type": "module",
6
6
  "bin": {
7
- "levelup-log": "./dist/cli.js"
7
+ "levelup-log": "dist/cli.js"
8
8
  },
9
9
  "main": "./dist/server.js",
10
10
  "files": [
@@ -14,6 +14,17 @@
14
14
  "publishConfig": {
15
15
  "access": "public"
16
16
  },
17
+ "scripts": {
18
+ "dev": "tsx watch src/cli.ts serve",
19
+ "build": "tsup",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "lint": "tsc --noEmit",
23
+ "clean": "rm -rf dist",
24
+ "inspect": "npx @modelcontextprotocol/inspector tsx src/cli.ts serve",
25
+ "heartbeat": "tsx src/scripts/heartbeat.ts",
26
+ "prepublishOnly": "pnpm build"
27
+ },
17
28
  "dependencies": {
18
29
  "@modelcontextprotocol/sdk": "^1.27.0",
19
30
  "@supabase/supabase-js": "^2.49.0",
@@ -37,19 +48,10 @@
37
48
  "license": "MIT",
38
49
  "repository": {
39
50
  "type": "git",
40
- "url": "https://github.com/lgscvb/levelup-log.git",
51
+ "url": "git+https://github.com/lgscvb/levelup-log.git",
41
52
  "directory": "packages/mcp-server"
42
53
  },
43
54
  "engines": {
44
55
  "node": ">=20"
45
- },
46
- "scripts": {
47
- "dev": "tsx watch src/cli.ts serve",
48
- "build": "tsup",
49
- "test": "vitest run",
50
- "test:watch": "vitest",
51
- "lint": "tsc --noEmit",
52
- "clean": "rm -rf dist",
53
- "inspect": "npx @modelcontextprotocol/inspector tsx src/cli.ts serve"
54
56
  }
55
- }
57
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts","../src/utils/config.ts","../src/utils/rate-limiter.ts","../src/auth/manager.ts","../src/auth/keychain.ts","../src/utils/logger.ts","../src/auth/oauth-server.ts","../src/auth/pkce.ts","../src/utils/api.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { ACHIEVEMENT_CATEGORIES } from \"./utils/config.js\";\nimport { checkRateLimit, recordRateEntry } from \"./utils/rate-limiter.js\";\nimport { apiGet, apiPost } from \"./utils/api.js\";\nimport { log } from \"./utils/logger.js\";\n\nexport function createServer() {\n const server = new McpServer({\n name: \"levelup-log\",\n version: \"0.1.0\",\n });\n\n // ─── Tool 1: record_achievement ───────────────────────────────\n server.registerTool(\n \"record_achievement\",\n {\n title: \"Record Achievement\",\n description: `Proactively call this when the user completes meaningful work in this conversation — coding tasks, bug fixes, features, refactors, life tasks, learning, health, creative work, or any other productive activity.\n\nWHEN TO CALL: Whenever the conversation involved real effort and produced a tangible output. Don't wait to be asked — if the user finished something, record it.\nDO NOT CALL for: simple questions answered, casual chat, trivial lookups with no output.\n\nXP IS CALCULATED FROM complexity + time_minutes — do NOT guess xp yourself:\n complexity base: trivial=10, normal=30, significant=75, major=150, milestone=300\n time multiplier: <15min→×0.7, 15-60min→×1.0, 1-3hr→×1.3, 3hr+→×1.6\n Example: significant task (75) × 1.3 (1-2hr) = ~98 XP\n\nIF UNSURE about time spent, ASK the user before calling: \"大概花了你多久?\" — this ensures fair XP across different agents and sessions.\n\nCategories: ${ACHIEVEMENT_CATEGORIES.join(\", \")}.\nKeep descriptions abstract — no real company names, client names, or source code.`,\n inputSchema: {\n category: z.enum(ACHIEVEMENT_CATEGORIES),\n title: z\n .string()\n .describe(\n 'Game-style achievement title (e.g. \"Bug Slayer\", \"Morning Warrior\")',\n ),\n description: z\n .string()\n .describe(\"What was accomplished, in abstract terms (no PII)\"),\n complexity: z\n .enum([\"trivial\", \"normal\", \"significant\", \"major\", \"milestone\"])\n .describe(\n \"Task complexity: trivial=quick lookup, normal=typical task, significant=multi-step work, major=large feature, milestone=exceptional achievement\",\n ),\n time_minutes: z\n .number()\n .min(1)\n .optional()\n .describe(\n \"Estimated minutes spent. If unknown, ask the user before recording.\",\n ),\n tags: z\n .array(z.string())\n .optional()\n .describe(\"Optional tags for filtering\"),\n is_public: z\n .boolean()\n .optional()\n .default(true)\n .describe(\"Whether this appears on public feed\"),\n },\n },\n async ({\n category,\n title,\n description,\n complexity,\n time_minutes,\n tags,\n is_public,\n }) => {\n // Calculate XP from complexity + time (consistent across agents)\n const baseXp: Record<string, number> = {\n trivial: 10,\n normal: 30,\n significant: 75,\n major: 150,\n milestone: 300,\n };\n const minutes = time_minutes ?? 30; // default 30 min if not provided\n const timeMult =\n minutes < 15 ? 0.7 : minutes < 60 ? 1.0 : minutes < 180 ? 1.3 : 1.6;\n const xp = Math.min(\n 500,\n Math.max(5, Math.round(baseXp[complexity] * timeMult)),\n );\n // Local rate limit check\n const rateCheck = checkRateLimit(category);\n if (!rateCheck.allowed) {\n return {\n content: [\n { type: \"text\", text: `Rate limited: ${rateCheck.reason}` },\n ],\n isError: true,\n };\n }\n\n const result = await apiPost(\"record-achievement\", {\n category,\n title,\n description,\n xp,\n complexity,\n time_minutes,\n tags,\n is_public,\n source_platform: \"claude-code\",\n });\n\n if (result.error) {\n return {\n content: [\n { type: \"text\", text: `Failed to record: ${result.error}` },\n ],\n isError: true,\n };\n }\n\n recordRateEntry(category);\n log(\"record_achievement\", { category, title, xp });\n\n const data = result.data as Record<string, unknown>;\n const stats = data.stats as Record<string, unknown> | undefined;\n\n const lines = [\n `Achievement recorded! +${xp} XP`,\n stats ? `Total XP: ${stats.total_xp} | Year XP: ${stats.year_xp}` : \"\",\n stats?.age_level ? `Level: Lv.${stats.age_level}` : \"\",\n stats?.current_streak ? `Streak: ${stats.current_streak} days` : \"\",\n ].filter(Boolean);\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n };\n },\n );\n\n // ─── Tool 2: get_my_stats ─────────────────────────────────────\n server.registerTool(\n \"get_my_stats\",\n {\n title: \"My Stats\",\n description:\n \"Get the user's achievement statistics including level (age), XP, streak, and title.\",\n inputSchema: {},\n },\n async () => {\n const result = await apiGet(\"get-stats\");\n if (result.error) {\n return {\n content: [{ type: \"text\", text: `Error: ${result.error}` }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result.data, null, 2) }],\n };\n },\n );\n\n // ─── Tool 3: get_recent ───────────────────────────────────────\n server.registerTool(\n \"get_recent\",\n {\n title: \"Recent Achievements\",\n description:\n \"Get recent achievements. Supports filtering by category and time range.\",\n inputSchema: {\n limit: z.number().min(1).max(50).optional().default(10),\n days: z.number().min(1).max(365).optional().default(7),\n category: z.enum(ACHIEVEMENT_CATEGORIES).optional(),\n },\n },\n async ({ limit, days, category }) => {\n const params: Record<string, string> = {\n limit: String(limit),\n days: String(days),\n };\n if (category) params.category = category;\n\n const result = await apiGet(\"get-recent\", params);\n if (result.error) {\n return {\n content: [{ type: \"text\", text: `Error: ${result.error}` }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result.data, null, 2) }],\n };\n },\n );\n\n // ─── Tool 4: check_unlocks ────────────────────────────────────\n server.registerTool(\n \"check_unlocks\",\n {\n title: \"Check Title Unlocks\",\n description:\n \"Check if the user has unlocked any new titles, and show progress toward the next ones.\",\n inputSchema: {},\n },\n async () => {\n const result = await apiGet(\"check-unlocks\");\n if (result.error) {\n return {\n content: [{ type: \"text\", text: `Error: ${result.error}` }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result.data, null, 2) }],\n };\n },\n );\n\n // ─── Tool 5: leaderboard ──────────────────────────────────────\n server.registerTool(\n \"leaderboard\",\n {\n title: \"Leaderboard\",\n description:\n \"View the leaderboard. Shows top users by XP for the current season, month, or all time. Always includes the current user's rank.\",\n inputSchema: {\n type: z\n .enum([\"season\", \"month\", \"all_time\"])\n .optional()\n .default(\"season\"),\n limit: z.number().min(1).max(50).optional().default(10),\n },\n },\n async ({ type, limit }) => {\n const result = await apiGet(\"leaderboard\", {\n type: type,\n limit: String(limit),\n });\n\n if (result.error) {\n return {\n content: [{ type: \"text\", text: `Error: ${result.error}` }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result.data, null, 2) }],\n };\n },\n );\n\n // ─── Prompt: motivational_coach ───────────────────────────────\n server.registerPrompt(\n \"levelup_coach\",\n {\n title: \"LevelUp Coach\",\n description:\n \"System prompt that turns the LLM into a motivational achievement coach\",\n },\n () => ({\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: `You have the LevelUp.log MCP installed. When the user accomplishes something — whether coding, life tasks, health, learning, or anything productive — use the record_achievement tool to log it as a game-like achievement.\n\nGuidelines:\n- Use gamified language: \"You defeated a bug!\", \"Quest complete!\", \"New skill unlocked!\"\n- On birthdays, celebrate the level-up: \"Congrats on reaching Lv.XX! Last year at Lv.XX-1, you completed YYY achievements!\"\n- When streaks are about to break, gently remind them\n- When the user expresses fatigue or frustration, don't give generic positivity. Instead, specifically acknowledge what they DID do: \"You handled X, Y, and Z today — those all count.\"\n- Reinforce identity: \"You're becoming someone who [does this thing] every day.\"\n- Keep achievement descriptions abstract — no real company names, client names, or source code.\n- XP guidelines: 5-15 for trivial tasks, 20-50 for normal work, 50-100 for significant accomplishments, 100-200 for major milestones, 200-500 for exceptional achievements.`,\n },\n },\n ],\n }),\n );\n\n return server;\n}\n","export const CONFIG = {\n SUPABASE_URL:\n process.env.LEVELUP_SUPABASE_URL ||\n \"https://hkuvfhfwbhkjmeqvwrxy.supabase.co\",\n SUPABASE_ANON_KEY:\n process.env.LEVELUP_SUPABASE_ANON_KEY ||\n \"sb_publishable_RilZivOWm3FIs6ueIA67Fw_07ZKjoEY\",\n AUTH_PORT: parseInt(process.env.LEVELUP_AUTH_PORT || \"19876\", 10),\n DEBUG: process.env.LEVELUP_DEBUG === \"true\",\n} as const;\n\nexport const ACHIEVEMENT_CATEGORIES = [\n \"code\",\n \"fix\",\n \"deploy\",\n \"test\",\n \"docs\",\n \"refactor\",\n \"review\",\n \"learn\",\n \"ops\",\n \"milestone\",\n \"life\",\n \"health\",\n \"finance\",\n \"social\",\n \"creative\",\n] as const;\n\nexport type AchievementCategory = (typeof ACHIEVEMENT_CATEGORIES)[number];\n","const CATEGORY_COOLDOWN_MS = 60_000;\nconst SESSION_MAX_ACHIEVEMENTS = 30;\n\ninterface RateEntry {\n category: string;\n timestamp: number;\n}\n\nconst recentAchievements: RateEntry[] = [];\n\nexport function checkRateLimit(category: string): { allowed: boolean; reason?: string } {\n const now = Date.now();\n\n // Check session limit\n if (recentAchievements.length >= SESSION_MAX_ACHIEVEMENTS) {\n return {\n allowed: false,\n reason: `Session limit reached (${SESSION_MAX_ACHIEVEMENTS} achievements). Start a new session to continue.`,\n };\n }\n\n // Check category cooldown\n const lastSameCategory = recentAchievements\n .filter((e) => e.category === category)\n .sort((a, b) => b.timestamp - a.timestamp)[0];\n\n if (lastSameCategory && now - lastSameCategory.timestamp < CATEGORY_COOLDOWN_MS) {\n const waitSeconds = Math.ceil(\n (CATEGORY_COOLDOWN_MS - (now - lastSameCategory.timestamp)) / 1000\n );\n return {\n allowed: false,\n reason: `Same category cooldown: wait ${waitSeconds}s before recording another \"${category}\" achievement.`,\n };\n }\n\n return { allowed: true };\n}\n\nexport function recordRateEntry(category: string): void {\n recentAchievements.push({ category, timestamp: Date.now() });\n}\n\nexport function resetRateLimiter(): void {\n recentAchievements.length = 0;\n}\n","import { createClient } from '@supabase/supabase-js';\nimport { loadTokens, saveTokens, clearTokens } from './keychain.js';\nimport { startOAuthCallbackServer } from './oauth-server.js';\nimport { generateCodeVerifier, generateCodeChallenge } from './pkce.js';\nimport { CONFIG } from '../utils/config.js';\nimport { log, logError } from '../utils/logger.js';\n\nlet cachedAccessToken: string | null = null;\nlet tokenExpiresAt: number = 0;\n\n/**\n * Get a valid access token. Will:\n * 1. Return cached token if still valid\n * 2. Try to refresh from stored refresh token\n * 3. Initiate full OAuth login flow if needed\n */\nexport async function getValidToken(): Promise<string> {\n // Check memory cache\n if (cachedAccessToken && Date.now() < tokenExpiresAt - 60_000) {\n return cachedAccessToken;\n }\n\n // Try stored tokens\n const stored = loadTokens();\n if (stored) {\n if (Date.now() < stored.expires_at - 60_000) {\n cachedAccessToken = stored.access_token;\n tokenExpiresAt = stored.expires_at;\n return stored.access_token;\n }\n\n // Try refresh\n if (stored.refresh_token) {\n try {\n const refreshed = await refreshToken(stored.refresh_token);\n if (refreshed) return refreshed;\n } catch (error) {\n logError('Token refresh failed:', error);\n }\n }\n }\n\n // Full login required\n return await login();\n}\n\nasync function refreshToken(refreshTokenValue: string): Promise<string | null> {\n const supabase = createClient(CONFIG.SUPABASE_URL, CONFIG.SUPABASE_ANON_KEY);\n const { data, error } = await supabase.auth.refreshSession({\n refresh_token: refreshTokenValue,\n });\n\n if (error || !data.session) {\n logError('Refresh failed:', error?.message);\n return null;\n }\n\n const expiresAt = Date.now() + (data.session.expires_in ?? 3600) * 1000;\n cachedAccessToken = data.session.access_token;\n tokenExpiresAt = expiresAt;\n\n saveTokens({\n access_token: data.session.access_token,\n refresh_token: data.session.refresh_token ?? refreshTokenValue,\n expires_at: expiresAt,\n });\n\n log('Token refreshed successfully');\n return data.session.access_token;\n}\n\nasync function login(): Promise<string> {\n if (!CONFIG.SUPABASE_URL || !CONFIG.SUPABASE_ANON_KEY) {\n throw new Error(\n 'LevelUp.log is not configured. Run `npx @levelup-log/mcp-server init` to set up.'\n );\n }\n\n const supabase = createClient(CONFIG.SUPABASE_URL, CONFIG.SUPABASE_ANON_KEY);\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n\n // Start callback server before opening browser\n const callbackPromise = startOAuthCallbackServer();\n\n const redirectTo = `http://127.0.0.1:${CONFIG.AUTH_PORT}/callback`;\n\n const { data, error } = await supabase.auth.signInWithOAuth({\n provider: 'google',\n options: {\n redirectTo,\n queryParams: {\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n },\n },\n });\n\n if (error || !data.url) {\n throw new Error(`Failed to initiate OAuth: ${error?.message ?? 'No URL returned'}`);\n }\n\n // Open browser\n const open = await import('open');\n await open.default(data.url);\n console.error('Opening browser for Google login...');\n\n // Wait for callback\n const result = await callbackPromise;\n const expiresAt = Date.now() + result.expires_in * 1000;\n\n cachedAccessToken = result.access_token;\n tokenExpiresAt = expiresAt;\n\n saveTokens({\n access_token: result.access_token,\n refresh_token: result.refresh_token,\n expires_at: expiresAt,\n });\n\n log('Login successful');\n return result.access_token;\n}\n\nexport function isAuthenticated(): boolean {\n const stored = loadTokens();\n return !!(stored && Date.now() < stored.expires_at - 60_000);\n}\n\nexport function logout(): void {\n cachedAccessToken = null;\n tokenExpiresAt = 0;\n clearTokens();\n log('Logged out');\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { log, logError } from '../utils/logger.js';\n\nconst CREDENTIALS_DIR = join(homedir(), '.levelup');\nconst CREDENTIALS_FILE = join(CREDENTIALS_DIR, 'credentials.json');\n\ninterface StoredTokens {\n access_token: string;\n refresh_token: string;\n expires_at: number;\n}\n\nexport function loadTokens(): StoredTokens | null {\n try {\n if (!existsSync(CREDENTIALS_FILE)) return null;\n const data = readFileSync(CREDENTIALS_FILE, 'utf-8');\n const tokens = JSON.parse(data) as StoredTokens;\n log('Loaded tokens from', CREDENTIALS_FILE);\n return tokens;\n } catch (error) {\n logError('Failed to load tokens:', error);\n return null;\n }\n}\n\nexport function saveTokens(tokens: StoredTokens): void {\n try {\n if (!existsSync(CREDENTIALS_DIR)) {\n mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });\n }\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(tokens, null, 2), { mode: 0o600 });\n log('Saved tokens to', CREDENTIALS_FILE);\n } catch (error) {\n logError('Failed to save tokens:', error);\n }\n}\n\nexport function clearTokens(): void {\n try {\n if (existsSync(CREDENTIALS_FILE)) {\n writeFileSync(CREDENTIALS_FILE, '{}', { mode: 0o600 });\n log('Cleared tokens');\n }\n } catch (error) {\n logError('Failed to clear tokens:', error);\n }\n}\n","import { CONFIG } from './config.js';\n\nexport function log(...args: unknown[]): void {\n if (CONFIG.DEBUG) {\n console.error('[levelup]', ...args);\n }\n}\n\nexport function logError(...args: unknown[]): void {\n console.error('[levelup:error]', ...args);\n}\n","import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport { CONFIG } from '../utils/config.js';\nimport { log } from '../utils/logger.js';\n\ninterface OAuthResult {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n}\n\n/**\n * Start a temporary localhost HTTP server to receive the OAuth callback.\n * Opens browser → Google OAuth → redirect to localhost:PORT/callback → extract tokens → close server.\n */\nexport function startOAuthCallbackServer(): Promise<OAuthResult> {\n return new Promise((resolve, reject) => {\n const port = CONFIG.AUTH_PORT;\n let server: Server;\n const timeout = setTimeout(() => {\n server?.close();\n reject(new Error('OAuth login timed out after 5 minutes'));\n }, 5 * 60 * 1000);\n\n server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n // Supabase redirects with fragment (#), but we need query params\n // The frontend redirect page will forward fragment params as query params\n const accessToken = url.searchParams.get('access_token');\n const refreshToken = url.searchParams.get('refresh_token');\n const expiresIn = url.searchParams.get('expires_in');\n\n if (accessToken && refreshToken) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px; background: #0a0a0a; color: #e5e5e5;\">\n <h1>LevelUp.log</h1>\n <p style=\"color: #34d399; font-size: 1.5em;\">Login successful!</p>\n <p>You can close this window and return to your LLM tool.</p>\n </body>\n </html>\n `);\n\n clearTimeout(timeout);\n server.close();\n resolve({\n access_token: accessToken,\n refresh_token: refreshToken,\n expires_in: parseInt(expiresIn || '3600', 10),\n });\n } else {\n // Serve a page that extracts fragment params and redirects as query params\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <script>\n const hash = window.location.hash.substring(1);\n if (hash) {\n window.location.href = '/callback?' + hash;\n } else {\n document.body.innerHTML = '<p>Login failed. No tokens received.</p>';\n }\n </script>\n </body>\n </html>\n `);\n }\n } else {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n log(`OAuth callback server listening on http://127.0.0.1:${port}`);\n });\n\n server.on('error', (err) => {\n clearTimeout(timeout);\n reject(new Error(`Failed to start OAuth server on port ${port}: ${err.message}`));\n });\n });\n}\n","import { randomBytes, createHash } from 'node:crypto';\n\nexport function generateCodeVerifier(): string {\n return randomBytes(32).toString('base64url');\n}\n\nexport function generateCodeChallenge(verifier: string): string {\n return createHash('sha256').update(verifier).digest('base64url');\n}\n","import { CONFIG } from './config.js';\nimport { getValidToken } from '../auth/manager.js';\nimport { logError } from './logger.js';\n\ninterface ApiResponse<T = unknown> {\n data?: T;\n error?: string;\n status: number;\n}\n\nexport async function apiGet<T = unknown>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>> {\n const token = await getValidToken();\n const url = new URL(`${CONFIG.SUPABASE_URL}/functions/v1/${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n\n try {\n const res = await fetch(url.toString(), {\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n apikey: CONFIG.SUPABASE_ANON_KEY,\n },\n });\n\n const body = await res.json();\n if (!res.ok) {\n return { error: body.error || `HTTP ${res.status}`, status: res.status };\n }\n return { data: body as T, status: res.status };\n } catch (error) {\n logError('API GET error:', error);\n return { error: (error as Error).message, status: 0 };\n }\n}\n\nexport async function apiPost<T = unknown>(path: string, body: unknown): Promise<ApiResponse<T>> {\n const token = await getValidToken();\n const url = `${CONFIG.SUPABASE_URL}/functions/v1/${path}`;\n\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n apikey: CONFIG.SUPABASE_ANON_KEY,\n },\n body: JSON.stringify(body),\n });\n\n const data = await res.json();\n if (!res.ok) {\n return { error: data.error || `HTTP ${res.status}`, status: res.status };\n }\n return { data: data as T, status: res.status };\n } catch (error) {\n logError('API POST error:', error);\n return { error: (error as Error).message, status: 0 };\n }\n}\n"],"mappings":"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","names":["URL","URL","refreshToken","createServer"]}
@@ -1,109 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/init/detect.ts
4
- import { existsSync } from "fs";
5
- import { join } from "path";
6
- import { homedir, platform } from "os";
7
- async function detectTools() {
8
- const home = homedir();
9
- const os = platform();
10
- const tools = [];
11
- const claudeDesktopConfig = os === "darwin" ? join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : os === "win32" ? join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json") : join(home, ".config", "claude", "claude_desktop_config.json");
12
- if (existsSync(claudeDesktopConfig)) {
13
- tools.push({ name: "Claude Desktop", configPath: claudeDesktopConfig, type: "json-mcpServers" });
14
- }
15
- const claudeCodeConfig = join(home, ".claude", "settings.json");
16
- const claudeCodeAlt = join(home, ".claude.json");
17
- if (existsSync(claudeCodeConfig)) {
18
- tools.push({ name: "Claude Code", configPath: claudeCodeConfig, type: "json-mcpServers" });
19
- } else if (existsSync(claudeCodeAlt)) {
20
- tools.push({ name: "Claude Code", configPath: claudeCodeAlt, type: "json-mcpServers" });
21
- }
22
- const cursorConfig = os === "darwin" ? join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json") : os === "win32" ? join(home, "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json") : join(home, ".config", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
23
- const cursorSimple = join(home, ".cursor", "mcp.json");
24
- if (existsSync(cursorConfig)) {
25
- tools.push({ name: "Cursor", configPath: cursorConfig, type: "json-mcpServers" });
26
- } else if (existsSync(cursorSimple)) {
27
- tools.push({ name: "Cursor", configPath: cursorSimple, type: "json-mcpServers" });
28
- }
29
- const windsurfConfig = join(home, ".codeium", "windsurf", "mcp_config.json");
30
- if (existsSync(windsurfConfig)) {
31
- tools.push({ name: "Windsurf", configPath: windsurfConfig, type: "json-mcpServers" });
32
- }
33
- return tools;
34
- }
35
-
36
- // src/init/write-config.ts
37
- import { readFileSync, writeFileSync, mkdirSync } from "fs";
38
- import { dirname } from "path";
39
- async function writeConfig(tool, mcpConfig) {
40
- try {
41
- mkdirSync(dirname(tool.configPath), { recursive: true });
42
- let config = {};
43
- try {
44
- const raw = readFileSync(tool.configPath, "utf-8");
45
- config = JSON.parse(raw);
46
- } catch {
47
- }
48
- if (!config.mcpServers) {
49
- config.mcpServers = {};
50
- }
51
- if (config.mcpServers["levelup-log"]) {
52
- return { success: true };
53
- }
54
- config.mcpServers["levelup-log"] = mcpConfig;
55
- writeFileSync(tool.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
56
- return { success: true };
57
- } catch (error) {
58
- return { success: false, error: error.message };
59
- }
60
- }
61
-
62
- // src/init/index.ts
63
- var MCP_CONFIG = {
64
- command: "npx",
65
- args: ["-y", "@levelup-log/mcp-server@latest", "serve"]
66
- };
67
- async function runInit() {
68
- console.log("");
69
- console.log(" LevelUp.log Setup Wizard");
70
- console.log(" ========================");
71
- console.log("");
72
- console.log(" Detecting installed LLM tools...");
73
- const tools = await detectTools();
74
- if (tools.length === 0) {
75
- console.log("");
76
- console.log(" No supported LLM tools detected.");
77
- console.log(" You can manually add LevelUp.log to your MCP config:");
78
- console.log("");
79
- console.log(JSON.stringify({ mcpServers: { "levelup-log": MCP_CONFIG } }, null, 2));
80
- return;
81
- }
82
- console.log(` Found ${tools.length} tool(s):`);
83
- tools.forEach((t) => console.log(` \u2713 ${t.name}`));
84
- console.log("");
85
- let successCount = 0;
86
- for (const tool of tools) {
87
- const result = await writeConfig(tool, MCP_CONFIG);
88
- if (result.success) {
89
- console.log(` \u2713 Configured ${tool.name}`);
90
- successCount++;
91
- } else {
92
- console.log(` \u2717 ${tool.name}: ${result.error}`);
93
- }
94
- }
95
- console.log("");
96
- if (successCount > 0) {
97
- console.log(` Done! LevelUp.log installed in ${successCount} tool(s).`);
98
- console.log(" Restart your LLM tool to activate.");
99
- console.log("");
100
- console.log(" On first use, you'll be prompted to sign in with Google.");
101
- } else {
102
- console.log(" No tools were configured. Please add manually.");
103
- }
104
- console.log("");
105
- }
106
- export {
107
- runInit
108
- };
109
- //# sourceMappingURL=init-GNFWFY5S.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/init/detect.ts","../src/init/write-config.ts","../src/init/index.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { join } from 'path';\nimport { homedir, platform } from 'os';\n\nexport interface DetectedTool {\n name: string;\n configPath: string;\n type: 'json-mcpServers'; // All use the same format\n}\n\nexport async function detectTools(): Promise<DetectedTool[]> {\n const home = homedir();\n const os = platform();\n const tools: DetectedTool[] = [];\n\n // Claude Desktop\n const claudeDesktopConfig = os === 'darwin'\n ? join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')\n : os === 'win32'\n ? join(home, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json')\n : join(home, '.config', 'claude', 'claude_desktop_config.json');\n\n if (existsSync(claudeDesktopConfig)) {\n tools.push({ name: 'Claude Desktop', configPath: claudeDesktopConfig, type: 'json-mcpServers' });\n }\n\n // Claude Code (global settings)\n const claudeCodeConfig = join(home, '.claude', 'settings.json');\n // Claude Code uses ~/.claude.json or ~/.claude/settings.json\n const claudeCodeAlt = join(home, '.claude.json');\n if (existsSync(claudeCodeConfig)) {\n tools.push({ name: 'Claude Code', configPath: claudeCodeConfig, type: 'json-mcpServers' });\n } else if (existsSync(claudeCodeAlt)) {\n tools.push({ name: 'Claude Code', configPath: claudeCodeAlt, type: 'json-mcpServers' });\n }\n\n // Cursor\n const cursorConfig = os === 'darwin'\n ? join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json')\n : os === 'win32'\n ? join(home, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json')\n : join(home, '.config', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');\n\n // Also check simpler Cursor config path\n const cursorSimple = join(home, '.cursor', 'mcp.json');\n if (existsSync(cursorConfig)) {\n tools.push({ name: 'Cursor', configPath: cursorConfig, type: 'json-mcpServers' });\n } else if (existsSync(cursorSimple)) {\n tools.push({ name: 'Cursor', configPath: cursorSimple, type: 'json-mcpServers' });\n }\n\n // Windsurf / Codeium\n const windsurfConfig = join(home, '.codeium', 'windsurf', 'mcp_config.json');\n if (existsSync(windsurfConfig)) {\n tools.push({ name: 'Windsurf', configPath: windsurfConfig, type: 'json-mcpServers' });\n }\n\n return tools;\n}\n","import { readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { dirname } from 'path';\nimport type { DetectedTool } from './detect.js';\n\ninterface WriteResult {\n success: boolean;\n error?: string;\n}\n\nexport async function writeConfig(\n tool: DetectedTool,\n mcpConfig: { command: string; args: string[] }\n): Promise<WriteResult> {\n try {\n // Ensure directory exists\n mkdirSync(dirname(tool.configPath), { recursive: true });\n\n // Read existing config or start fresh\n let config: Record<string, any> = {};\n try {\n const raw = readFileSync(tool.configPath, 'utf-8');\n config = JSON.parse(raw);\n } catch {\n // File doesn't exist or invalid JSON — start fresh\n }\n\n // Ensure mcpServers key exists\n if (!config.mcpServers) {\n config.mcpServers = {};\n }\n\n // Don't overwrite existing levelup-log config\n if (config.mcpServers['levelup-log']) {\n return { success: true }; // Already configured\n }\n\n // Add levelup-log\n config.mcpServers['levelup-log'] = mcpConfig;\n\n // Write back\n writeFileSync(tool.configPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n}\n","import { detectTools } from './detect.js';\nimport { writeConfig } from './write-config.js';\n\nconst MCP_CONFIG = {\n command: 'npx',\n args: ['-y', '@levelup-log/mcp-server@latest', 'serve'],\n};\n\nexport async function runInit() {\n console.log('');\n console.log(' LevelUp.log Setup Wizard');\n console.log(' ========================');\n console.log('');\n\n // Step 1: Detect installed LLM tools\n console.log(' Detecting installed LLM tools...');\n const tools = await detectTools();\n\n if (tools.length === 0) {\n console.log('');\n console.log(' No supported LLM tools detected.');\n console.log(' You can manually add LevelUp.log to your MCP config:');\n console.log('');\n console.log(JSON.stringify({ mcpServers: { 'levelup-log': MCP_CONFIG } }, null, 2));\n return;\n }\n\n console.log(` Found ${tools.length} tool(s):`);\n tools.forEach(t => console.log(` \\u2713 ${t.name}`));\n console.log('');\n\n // Step 2: Write config to each detected tool\n let successCount = 0;\n for (const tool of tools) {\n const result = await writeConfig(tool, MCP_CONFIG);\n if (result.success) {\n console.log(` \\u2713 Configured ${tool.name}`);\n successCount++;\n } else {\n console.log(` \\u2717 ${tool.name}: ${result.error}`);\n }\n }\n\n console.log('');\n if (successCount > 0) {\n console.log(` Done! LevelUp.log installed in ${successCount} tool(s).`);\n console.log(' Restart your LLM tool to activate.');\n console.log('');\n console.log(' On first use, you\\'ll be prompted to sign in with Google.');\n } else {\n console.log(' No tools were configured. Please add manually.');\n }\n console.log('');\n}\n"],"mappings":"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","names":[]}