slima-mcp 0.1.8 → 0.1.10

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/dist/index.js CHANGED
@@ -328,6 +328,89 @@ var SlimaApiClient = class {
328
328
 
329
329
  // src/core/tools/books.ts
330
330
  import { z } from "zod";
331
+
332
+ // src/core/utils/file-tree.ts
333
+ function getParentToken(file) {
334
+ return file.parentToken || file.parent_token;
335
+ }
336
+ function getWordCount(file) {
337
+ return file.wordCount ?? file.word_count;
338
+ }
339
+ function getIsManuscript(file) {
340
+ return file.isManuscript ?? file.is_manuscript;
341
+ }
342
+ function getBlobHash(file) {
343
+ return file.blobHash || file.blob_hash;
344
+ }
345
+ function buildFileTree(flatFiles) {
346
+ if (flatFiles.length === 0) {
347
+ return [];
348
+ }
349
+ const fileMap = /* @__PURE__ */ new Map();
350
+ for (const file of flatFiles) {
351
+ fileMap.set(file.token, {
352
+ token: file.token,
353
+ name: file.name,
354
+ kind: file.kind,
355
+ position: file.position,
356
+ blobHash: getBlobHash(file),
357
+ wordCount: getWordCount(file),
358
+ isManuscript: getIsManuscript(file),
359
+ children: []
360
+ });
361
+ }
362
+ const rootFiles = [];
363
+ for (const file of flatFiles) {
364
+ const node = fileMap.get(file.token);
365
+ const parentToken = getParentToken(file);
366
+ if (parentToken) {
367
+ const parent = fileMap.get(parentToken);
368
+ if (parent) {
369
+ parent.children = parent.children || [];
370
+ parent.children.push(node);
371
+ } else {
372
+ rootFiles.push(node);
373
+ }
374
+ } else {
375
+ rootFiles.push(node);
376
+ }
377
+ }
378
+ sortChildrenByPosition(rootFiles);
379
+ cleanEmptyChildren(rootFiles);
380
+ return rootFiles;
381
+ }
382
+ function sortChildrenByPosition(files) {
383
+ files.sort((a, b) => a.position - b.position);
384
+ for (const file of files) {
385
+ if (file.children && file.children.length > 0) {
386
+ sortChildrenByPosition(file.children);
387
+ }
388
+ }
389
+ }
390
+ function cleanEmptyChildren(files) {
391
+ for (const file of files) {
392
+ if (file.children && file.children.length === 0) {
393
+ delete file.children;
394
+ } else if (file.children) {
395
+ cleanEmptyChildren(file.children);
396
+ }
397
+ }
398
+ }
399
+ function formatFileTree(files, indent = 0) {
400
+ return files.sort((a, b) => a.position - b.position).map((f) => {
401
+ const prefix = " ".repeat(indent);
402
+ const icon = f.kind === "folder" ? "\u{1F4C1}" : "\u{1F4C4}";
403
+ const words = f.kind !== "folder" && f.wordCount ? ` (${f.wordCount} words)` : "";
404
+ const manuscript = f.isManuscript ? " [M]" : "";
405
+ let line = `${prefix}${icon} ${f.name}${words}${manuscript}`;
406
+ if (f.children && f.children.length > 0) {
407
+ line += "\n" + formatFileTree(f.children, indent + 1);
408
+ }
409
+ return line;
410
+ }).join("\n");
411
+ }
412
+
413
+ // src/core/tools/books.ts
331
414
  function registerBookTools(server, client) {
332
415
  server.tool(
333
416
  "create_book",
@@ -455,8 +538,9 @@ ${formatted}`
455
538
  ]
456
539
  };
457
540
  }
458
- const snapshot = commits[0].filesSnapshot;
459
- const tree = formatFileTree(snapshot);
541
+ const flatSnapshot = commits[0].filesSnapshot;
542
+ const nestedTree = buildFileTree(flatSnapshot);
543
+ const tree = formatFileTree(nestedTree);
460
544
  return {
461
545
  content: [
462
546
  {
@@ -514,19 +598,6 @@ ${tree}`
514
598
  }
515
599
  );
516
600
  }
517
- function formatFileTree(files, indent = 0) {
518
- return files.sort((a, b) => a.position - b.position).map((f) => {
519
- const prefix = " ".repeat(indent);
520
- const icon = f.kind === "folder" ? "\u{1F4C1}" : "\u{1F4C4}";
521
- const words = f.kind === "file" && f.wordCount ? ` (${f.wordCount} words)` : "";
522
- const manuscript = f.isManuscript ? " [M]" : "";
523
- let line = `${prefix}${icon} ${f.name}${words}${manuscript}`;
524
- if (f.children && f.children.length > 0) {
525
- line += "\n" + formatFileTree(f.children, indent + 1);
526
- }
527
- return line;
528
- }).join("\n");
529
- }
530
601
 
531
602
  // src/core/tools/content.ts
532
603
  import { z as z2 } from "zod";
@@ -1437,7 +1508,7 @@ function createSlimaServer(config) {
1437
1508
  });
1438
1509
  const server = new McpServer({
1439
1510
  name: "slima",
1440
- version: "0.1.8"
1511
+ version: "0.1.10"
1441
1512
  });
1442
1513
  registerBookTools(server, client);
1443
1514
  registerContentTools(server, client);
@@ -1889,7 +1960,7 @@ async function runStatus() {
1889
1960
  }
1890
1961
 
1891
1962
  // src/cli/index.ts
1892
- var VERSION = "0.1.8";
1963
+ var VERSION = "0.1.10";
1893
1964
  function showHelp() {
1894
1965
  console.log(`
1895
1966
  Slima MCP v${VERSION}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts","../src/cli/server.ts","../src/core/utils/errors.ts","../src/core/api/client.ts","../src/core/tools/books.ts","../src/core/tools/content.ts","../src/core/tools/beta-reader.ts","../src/core/tools/files.ts","../src/core/utils/logger.ts","../src/cli/token-storage.ts","../src/cli/auth.ts"],"sourcesContent":["/**\n * Slima MCP Server - CLI Entry Point\n *\n * Usage:\n * slima-mcp - Start MCP server (requires SLIMA_API_TOKEN or prior auth)\n * slima-mcp auth - Authenticate with Slima (opens browser)\n * slima-mcp logout - Remove saved credentials\n * slima-mcp status - Show authentication status\n * slima-mcp --help - Show help\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createSlimaServer } from './server.js';\nimport { logger } from '../core/utils/logger.js';\nimport { loadToken, getCredentialsFilePath } from './token-storage.js';\nimport { runAuth, runLogout, runStatus } from './auth.js';\n\ndeclare const __VERSION__: string;\nconst VERSION = __VERSION__;\n\nfunction showHelp(): void {\n console.log(`\nSlima MCP v${VERSION}\nMCP Server for Slima - AI Writing IDE for Novel Authors\n\nUSAGE:\n slima-mcp [command]\n\nCOMMANDS:\n (none) Start the MCP server\n auth Authenticate with Slima (opens browser)\n auth --force Re-authenticate (switch account)\n logout Remove saved credentials\n status Show authentication status\n --help Show this help message\n --version Show version\n\nAUTHENTICATION:\n 1. Run: slima-mcp auth\n 2. Complete authentication in browser\n 3. Token is automatically saved\n\n Or set SLIMA_API_TOKEN environment variable.\n\nEXAMPLES:\n slima-mcp auth # Authenticate with browser\n slima-mcp auth --force # Re-authenticate (switch account)\n slima-mcp status # Check auth status\n SLIMA_API_TOKEN=xxx slima-mcp # Use env var\n\nCONFIGURATION (for AI tools):\n Claude Desktop, Cursor, etc.:\n {\n \"mcpServers\": {\n \"slima\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"slima-mcp\"]\n }\n }\n }\n\nMORE INFO:\n https://github.com/slima-ai/slima-mcp\n`);\n}\n\nfunction showVersion(): void {\n console.log(`slima-mcp v${VERSION}`);\n}\n\nasync function startServer(): Promise<void> {\n // Use env var first, then stored token\n let apiToken = process.env.SLIMA_API_TOKEN;\n let baseUrl = process.env.SLIMA_API_URL;\n\n if (!apiToken) {\n const credentials = await loadToken();\n if (credentials) {\n apiToken = credentials.apiToken;\n baseUrl = baseUrl || credentials.baseUrl;\n }\n }\n\n if (!apiToken) {\n logger.error('Not authenticated and SLIMA_API_TOKEN not set');\n logger.error('');\n logger.error('To authenticate, run:');\n logger.error(' slima-mcp auth');\n logger.error('');\n logger.error('Or set environment variable:');\n logger.error(' SLIMA_API_TOKEN=your_token slima-mcp');\n logger.error('');\n logger.error('Get your API token from: https://app.slima.ai/settings/api-tokens');\n process.exit(1);\n }\n\n try {\n const server = createSlimaServer({\n apiToken,\n baseUrl,\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n logger.info('Slima MCP Server running on stdio');\n\n // Graceful shutdown\n process.on('SIGINT', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n logger.error('Failed to start server', error);\n process.exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const command = args[0];\n\n switch (command) {\n case 'auth': {\n const forceFlag = args.includes('--force') || args.includes('-f');\n await runAuth({ force: forceFlag });\n break;\n }\n\n case 'logout':\n await runLogout();\n break;\n\n case 'status':\n await runStatus();\n break;\n\n case '--help':\n case '-h':\n case 'help':\n showHelp();\n break;\n\n case '--version':\n case '-v':\n showVersion();\n break;\n\n case undefined:\n // Default: start MCP server\n await startServer();\n break;\n\n default:\n console.error(`Unknown command: ${command}`);\n console.error('Run `slima-mcp --help` for usage.');\n process.exit(1);\n }\n}\n\nmain();\n","/**\n * Slima MCP Server (CLI/stdio)\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { SlimaApiClient } from '../core/api/client.js';\nimport {\n registerBookTools,\n registerContentTools,\n registerBetaReaderTools,\n registerFileTools,\n} from '../core/tools/index.js';\nimport { logger } from '../core/utils/logger.js';\n\ndeclare const __VERSION__: string;\n\nexport interface ServerConfig {\n apiToken: string;\n baseUrl?: string;\n}\n\n/**\n * Create Slima MCP Server\n */\nexport function createSlimaServer(config: ServerConfig): McpServer {\n // Create API Client with static token getter (for CLI)\n const client = new SlimaApiClient({\n baseUrl: config.baseUrl || 'https://api.slima.ai',\n getToken: async () => config.apiToken,\n logger,\n });\n\n const server = new McpServer({\n name: 'slima',\n version: __VERSION__,\n });\n\n // Register tools\n registerBookTools(server, client);\n registerContentTools(server, client);\n registerBetaReaderTools(server, client, logger);\n registerFileTools(server, client);\n\n logger.info('Slima MCP Server initialized');\n logger.info(`API endpoint: ${config.baseUrl || 'https://api.slima.ai'}`);\n\n return server;\n}\n","/**\n * 錯誤處理工具\n */\n\nexport class SlimaApiError extends Error {\n constructor(\n public status: number,\n public code?: string,\n message?: string\n ) {\n super(message || `API Error: ${status}`);\n this.name = 'SlimaApiError';\n }\n}\n\nexport class AuthenticationError extends SlimaApiError {\n constructor(message = 'Invalid or expired API token') {\n super(401, 'UNAUTHORIZED', message);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class NotFoundError extends SlimaApiError {\n constructor(resource = 'Resource') {\n super(404, 'NOT_FOUND', `${resource} not found`);\n this.name = 'NotFoundError';\n }\n}\n\nexport class InsufficientCreditsError extends SlimaApiError {\n constructor() {\n super(402, 'INSUFFICIENT_CREDITS', 'Insufficient credits');\n this.name = 'InsufficientCreditsError';\n }\n}\n\n/**\n * 格式化錯誤訊息供 MCP 回傳\n */\nexport function formatErrorForMcp(error: unknown): string {\n if (error instanceof SlimaApiError) {\n switch (error.code) {\n case 'NOT_FOUND':\n return 'Resource not found. Please check the token.';\n case 'UNAUTHORIZED':\n return 'Invalid API token. Please check your configuration.';\n case 'INSUFFICIENT_CREDITS':\n return 'Insufficient credits. Please top up your account.';\n case 'TOKEN_EXPIRED':\n return 'API token has expired. Please generate a new one.';\n case 'TOKEN_REVOKED':\n return 'API token has been revoked. Please generate a new one.';\n default:\n return error.message;\n }\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return 'An unexpected error occurred.';\n}\n","/**\n * Slima API Client\n *\n * Supports dependency injection for token retrieval,\n * allowing different strategies for CLI (file-based) vs Worker (KV-based).\n */\n\nimport {\n type Book,\n type Commit,\n type Branch,\n type Blob,\n type Persona,\n type ReaderTest,\n type ReaderTestProgress,\n type CreateReaderTestParams,\n type ApiResponse,\n type ApiError,\n type CommitsListResponse,\n type BlobsDownloadResponse,\n type McpFileReadResponse,\n type McpFileCreateResponse,\n type McpFileUpdateResponse,\n type McpFileDeleteResponse,\n type McpFileAppendResponse,\n type McpSearchResponse,\n} from './types.js';\nimport {\n SlimaApiError,\n AuthenticationError,\n NotFoundError,\n InsufficientCreditsError,\n} from '../utils/errors.js';\n\n// Export for logger injection (optional)\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, error?: unknown) => void;\n}\n\n// Default no-op logger\nconst defaultLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * API Client Configuration (new style with token getter)\n */\nexport interface ApiClientConfig {\n baseUrl: string;\n getToken: () => Promise<string>; // Async token getter (injected)\n logger?: Logger;\n}\n\n/**\n * Legacy config format (backwards compatibility)\n * @deprecated Use ApiClientConfig with getToken instead\n */\nexport interface SlimaConfig {\n baseUrl: string;\n token: string;\n}\n\n// Type guard to check if config is legacy format\nfunction isLegacyConfig(config: ApiClientConfig | SlimaConfig): config is SlimaConfig {\n return 'token' in config && typeof (config as SlimaConfig).token === 'string';\n}\n\nexport class SlimaApiClient {\n private readonly baseUrl: string;\n private readonly getToken: () => Promise<string>;\n private readonly logger: Logger;\n\n constructor(config: ApiClientConfig | SlimaConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\n // Support both legacy and new config formats\n if (isLegacyConfig(config)) {\n const staticToken = config.token;\n this.getToken = async () => staticToken;\n this.logger = defaultLogger;\n } else {\n this.getToken = config.getToken;\n this.logger = config.logger || defaultLogger;\n }\n }\n\n /**\n * Send API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const token = await this.getToken();\n\n this.logger.debug(`${method} ${path}`);\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'slima-mcp-server/0.1.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const json = (await response.json()) as ApiResponse<T>;\n return json.data;\n }\n\n /**\n * Handle error response\n */\n private async handleErrorResponse(response: Response): Promise<never> {\n let errorData: ApiError | undefined;\n\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n // Cannot parse JSON\n }\n\n const code = errorData?.error?.code;\n const message = errorData?.error?.message;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 404:\n throw new NotFoundError(message);\n case 402:\n throw new InsufficientCreditsError();\n default:\n throw new SlimaApiError(response.status, code, message);\n }\n }\n\n // === Book Operations ===\n\n /**\n * List all books\n */\n async listBooks(): Promise<Book[]> {\n return this.request<Book[]>('GET', '/api/v1/books');\n }\n\n /**\n * Get book details\n */\n async getBook(token: string): Promise<Book> {\n return this.request<Book>('GET', `/api/v1/books/${token}`);\n }\n\n /**\n * Create a new book\n */\n async createBook(params: {\n title: string;\n authorName?: string;\n description?: string;\n }): Promise<Book> {\n return this.request<Book>('POST', '/api/v1/books', {\n book: {\n title: params.title,\n author_name: params.authorName,\n description: params.description,\n },\n });\n }\n\n // === Version Control ===\n\n /**\n * List commits\n */\n async listCommits(bookToken: string, limit = 10): Promise<Commit[]> {\n const response = await this.request<CommitsListResponse>(\n 'GET',\n `/api/v1/books/${bookToken}/commits?limit=${limit}`\n );\n return response.commits;\n }\n\n /**\n * Get commit details\n */\n async getCommit(bookToken: string, commitToken: string): Promise<Commit> {\n return this.request<Commit>(\n 'GET',\n `/api/v1/books/${bookToken}/commits/${commitToken}`\n );\n }\n\n /**\n * List branches\n */\n async listBranches(bookToken: string): Promise<Branch[]> {\n return this.request<Branch[]>('GET', `/api/v1/books/${bookToken}/branches`);\n }\n\n /**\n * Download blobs\n */\n async downloadBlobs(bookToken: string, hashes: string[]): Promise<Blob[]> {\n const response = await this.request<BlobsDownloadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/blobs/download`,\n { hashes }\n );\n return response.blobs;\n }\n\n // === Persona ===\n\n /**\n * List personas\n */\n async listPersonas(genre?: string): Promise<Persona[]> {\n const params = genre ? `?genre=${encodeURIComponent(genre)}` : '';\n return this.request<Persona[]>('GET', `/api/v1/personas${params}`);\n }\n\n /**\n * Get persona details\n */\n async getPersona(token: string): Promise<Persona> {\n return this.request<Persona>('GET', `/api/v1/personas/${token}`);\n }\n\n // === Reader Test ===\n\n /**\n * Create reader test\n */\n async createReaderTest(\n bookToken: string,\n params: CreateReaderTestParams\n ): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'POST',\n `/api/v1/books/${bookToken}/reader_tests`,\n {\n reader_test: {\n report_type: params.reportType || 'chapter',\n persona_tokens: params.personaTokens,\n commit_token: params.commitToken,\n model: params.model,\n scope_config: params.scopeConfig,\n },\n content: params.content,\n }\n );\n }\n\n /**\n * Get reader test progress\n */\n async getReaderTestProgress(\n bookToken: string,\n testToken: string\n ): Promise<ReaderTestProgress> {\n return this.request<ReaderTestProgress>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}/progress`\n );\n }\n\n /**\n * Get reader test result\n */\n async getReaderTest(bookToken: string, testToken: string): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}`\n );\n }\n\n // === MCP File Operations ===\n\n /**\n * Read file content\n */\n async readFile(bookToken: string, path: string): Promise<McpFileReadResponse> {\n return this.request<McpFileReadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/read`,\n { path }\n );\n }\n\n /**\n * Create file\n */\n async createFile(\n bookToken: string,\n params: { path: string; content?: string; parentPath?: string; commitMessage?: string }\n ): Promise<McpFileCreateResponse> {\n return this.request<McpFileCreateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/create`,\n {\n path: params.path,\n content: params.content || '',\n parent_path: params.parentPath,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Update (overwrite) file content\n */\n async updateFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileUpdateResponse> {\n return this.request<McpFileUpdateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/update`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Delete file\n */\n async deleteFile(\n bookToken: string,\n params: { path: string; commitMessage?: string }\n ): Promise<McpFileDeleteResponse> {\n return this.request<McpFileDeleteResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/delete`,\n {\n path: params.path,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Append content to file\n */\n async appendToFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileAppendResponse> {\n return this.request<McpFileAppendResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/append`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Search file content\n */\n async searchFiles(\n bookToken: string,\n params: { query: string; fileTypes?: string[]; limit?: number }\n ): Promise<McpSearchResponse> {\n return this.request<McpSearchResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/search`,\n {\n query: params.query,\n file_types: params.fileTypes,\n limit: params.limit,\n }\n );\n }\n}\n","/**\n * Book Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * Register book-related tools\n */\nexport function registerBookTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === create_book ===\n server.tool(\n 'create_book',\n 'Create a new book in your Slima library',\n {\n title: z.string().describe('Book title (required)'),\n author_name: z.string().optional().describe('Author name (optional)'),\n description: z.string().optional().describe('Book description (optional)'),\n },\n async ({ title, author_name, description }) => {\n try {\n const book = await client.createBook({\n title,\n authorName: author_name,\n description,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ Book created successfully!\\n\\n- **Title**: ${book.title}\\n- **Token**: ${book.token}\\n- **Author**: ${book.authorName || 'N/A'}\\n\\nYou can now add files using create_file with book_token: ${book.token}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === list_books ===\n server.tool(\n 'list_books',\n 'List all books in your Slima library',\n {},\n async () => {\n try {\n const books = await client.listBooks();\n\n if (books.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No books found in your library.',\n },\n ],\n };\n }\n\n const formatted = books\n .map((book) => {\n const words = book.totalWordCount?.toLocaleString() || '0';\n return `- **${book.title}** (${book.token})\\n Author: ${book.authorName || 'N/A'} | Words: ${words}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${books.length} book(s):\\n\\n${formatted}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book ===\n server.tool(\n 'get_book',\n 'Get details of a specific book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n },\n async ({ book_token }) => {\n try {\n const book = await client.getBook(book_token);\n\n const text = `# ${book.title}\n\n- **Token**: ${book.token}\n- **Author**: ${book.authorName || 'N/A'}\n- **Description**: ${book.description || 'N/A'}\n- **Language**: ${book.language || 'N/A'}\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Created**: ${book.createdAt}\n- **Updated**: ${book.updatedAt}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book_structure ===\n server.tool(\n 'get_book_structure',\n 'Get the file/folder structure of a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n const snapshot = commits[0].filesSnapshot;\n const tree = formatFileTree(snapshot);\n\n return {\n content: [\n {\n type: 'text',\n text: `File structure for book ${book_token}:\\n\\n${tree}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_writing_stats ===\n server.tool(\n 'get_writing_stats',\n 'Get writing statistics for a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const [book, commits] = await Promise.all([\n client.getBook(book_token),\n client.listCommits(book_token, 10),\n ]);\n\n // Calculate recent progress\n let wordChange = 0;\n if (commits.length > 1) {\n const latest = commits[0].totalWordCount || 0;\n const oldest = commits[commits.length - 1].totalWordCount || 0;\n wordChange = latest - oldest;\n }\n\n const sign = wordChange >= 0 ? '+' : '';\n const progressText =\n commits.length > 1\n ? `${sign}${wordChange.toLocaleString()} words (last ${commits.length} commits)`\n : 'N/A (not enough history)';\n\n const text = `# Writing Stats: ${book.title}\n\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Recent Progress**: ${progressText}\n- **Commits Analyzed**: ${commits.length}\n- **Language**: ${book.language || 'N/A'}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Format file tree\n */\nfunction formatFileTree(files: FileSnapshot[], indent = 0): string {\n return files\n .sort((a, b) => a.position - b.position)\n .map((f) => {\n const prefix = ' '.repeat(indent);\n const icon = f.kind === 'folder' ? '📁' : '📄';\n const words = f.kind === 'file' && f.wordCount ? ` (${f.wordCount} words)` : '';\n const manuscript = f.isManuscript ? ' [M]' : '';\n let line = `${prefix}${icon} ${f.name}${words}${manuscript}`;\n\n if (f.children && f.children.length > 0) {\n line += '\\n' + formatFileTree(f.children, indent + 1);\n }\n\n return line;\n })\n .join('\\n');\n}\n","/**\n * Content Access Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * Register content access tools\n */\nexport function registerContentTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === get_chapter ===\n server.tool(\n 'get_chapter',\n 'Read the content of a chapter or file from a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('File path (e.g., /chapters/01.md or chapter-01)'),\n },\n async ({ book_token, file_path }) => {\n try {\n // 1. Get latest commit\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n // 2. Find file in snapshot\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n // List available files\n const availableFiles = listAllFiles(commits[0].filesSnapshot);\n const fileList = availableFiles.slice(0, 10).join('\\n- ');\n const moreText =\n availableFiles.length > 10\n ? `\\n... and ${availableFiles.length - 10} more files`\n : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}\\n\\nAvailable files:\\n- ${fileList}${moreText}\\n\\nTip: Use get_book_structure to see the full file tree.`,\n },\n ],\n isError: true,\n };\n }\n\n if (file.kind === 'folder') {\n return {\n content: [\n {\n type: 'text',\n text: `\"${file_path}\" is a folder, not a file. Please specify a file path.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n };\n }\n\n // 3. Download blob content\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content. The blob may be missing.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n const wordInfo = file.wordCount ? ` (${file.wordCount} words)` : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `# ${file.name}${wordInfo}\\n\\n${content}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Find file in snapshot\n * Supports multiple path formats:\n * - Full path: /chapters/01.md\n * - Relative path: chapters/01.md\n * - Filename: 01.md\n * - Token: chapter-01\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n // Normalize path\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n // Recursive search\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n // Full path match\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n // Folder match, continue searching\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n // Also try searching in subfolders directly\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n // Try full path first\n let result = search(files, pathParts);\n if (result) return result;\n\n // Then try searching by filename only\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\n/**\n * Search by filename only\n */\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n\n/**\n * List all file paths\n */\nfunction listAllFiles(files: FileSnapshot[], prefix = ''): string[] {\n const result: string[] = [];\n\n for (const item of files) {\n const path = prefix ? `${prefix}/${item.name}` : item.name;\n\n if (item.kind === 'file') {\n result.push(path);\n } else if (item.children) {\n result.push(...listAllFiles(item.children, path));\n }\n }\n\n return result;\n}\n","/**\n * Beta Reader Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient, Logger } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot, ReaderTest, IndividualFeedback, AggregatedMetrics } from '../api/types.js';\n\n// Default no-op logger\nconst defaultLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Register Beta Reader tools\n */\nexport function registerBetaReaderTools(\n server: McpServer,\n client: SlimaApiClient,\n logger: Logger = defaultLogger\n): void {\n // === list_personas ===\n server.tool(\n 'list_personas',\n 'List available virtual reader personas for beta reading feedback',\n {\n genre: z\n .string()\n .optional()\n .describe('Filter by genre (e.g., fantasy, romance, scifi)'),\n },\n async ({ genre }) => {\n try {\n const personas = await client.listPersonas(genre);\n\n if (personas.length === 0) {\n const genreText = genre ? ` for genre \"${genre}\"` : '';\n return {\n content: [\n {\n type: 'text',\n text: `No personas found${genreText}.`,\n },\n ],\n };\n }\n\n const formatted = personas\n .map((p) => {\n const name = p.displayLabels?.zh || p.displayLabels?.en || p.slug;\n const tags = p.genreTags?.join(', ') || 'General';\n const demo = p.demographics || {};\n const gender = demo.gender || 'N/A';\n const age = demo.ageRange || 'N/A';\n\n return `- **${name}** (\\`${p.token}\\`)\n Gender: ${gender} | Age: ${age}\n Genres: ${tags}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Available Personas (${personas.length}):\\n\\n${formatted}\\n\\n**Tip**: Use the persona token with \\`analyze_chapter\\` to get feedback from a specific reader.`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === analyze_chapter ===\n server.tool(\n 'analyze_chapter',\n 'Get AI Beta Reader feedback on a chapter from a specific reader persona. This may take a moment to complete.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('Chapter file path (e.g., /chapters/01.md)'),\n persona_token: z\n .string()\n .describe('Persona token (e.g., psn_xxx). Use list_personas to find available personas.'),\n },\n async ({ book_token, file_path, persona_token }) => {\n try {\n // 1. Get latest commit and chapter content\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n isError: true,\n };\n }\n\n // 2. Find file\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}. Use get_book_structure to see available files.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. Download content\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n\n // 4. Create Reader Test\n logger.info(`Creating reader test for ${file_path} with persona ${persona_token}`);\n\n const test = await client.createReaderTest(book_token, {\n reportType: 'chapter',\n personaTokens: [persona_token],\n commitToken: commits[0].token,\n scopeConfig: { chapters: [file_path] },\n content: content,\n });\n\n // 5. Poll for completion (max 90 seconds)\n const result = await pollForCompletion(\n () => client.getReaderTestProgress(book_token, test.token),\n 90000,\n logger\n );\n\n if (result.status === 'failed') {\n return {\n content: [\n {\n type: 'text',\n text: 'Analysis failed. Please try again later.',\n },\n ],\n isError: true,\n };\n }\n\n if (result.status !== 'completed') {\n return {\n content: [\n {\n type: 'text',\n text: `Analysis is still in progress. You can check the status later.\\n\\nTest ID: ${test.token}`,\n },\n ],\n };\n }\n\n // 6. Get full result\n const fullResult = await client.getReaderTest(book_token, test.token);\n\n // 7. Format feedback\n const feedback = formatBetaReaderFeedback(fullResult, file.name);\n\n return {\n content: [{ type: 'text', text: feedback }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Poll for completion\n */\nasync function pollForCompletion(\n checkFn: () => Promise<{ status: string; progress?: number }>,\n timeoutMs: number,\n logger: Logger\n): Promise<{ status: string }> {\n const startTime = Date.now();\n const pollInterval = 3000; // 3 seconds\n\n while (Date.now() - startTime < timeoutMs) {\n const result = await checkFn();\n logger.debug(`Poll status: ${result.status}, progress: ${result.progress || 'N/A'}`);\n\n if (result.status === 'completed' || result.status === 'failed') {\n return result;\n }\n\n await sleep(pollInterval);\n }\n\n return { status: 'timeout' };\n}\n\n/**\n * Sleep\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Format Beta Reader feedback\n */\nfunction formatBetaReaderFeedback(test: ReaderTest, fileName: string): string {\n const feedbacks = test.individualFeedbacks || [];\n const metrics = test.aggregatedMetrics;\n\n if (feedbacks.length === 0 && !metrics) {\n return 'No feedback available.';\n }\n\n let output = `# Beta Reader Feedback: ${fileName}\\n\\n`;\n\n // Aggregated metrics summary\n if (metrics) {\n output += formatAggregatedMetrics(metrics);\n }\n\n // Individual persona feedback\n if (feedbacks.length > 0) {\n output += `\\n---\\n\\n## Individual Reader Feedback\\n\\n`;\n output += feedbacks.map(formatIndividualFeedback).join('\\n---\\n\\n');\n }\n\n return output;\n}\n\n/**\n * Format aggregated metrics\n */\nfunction formatAggregatedMetrics(metrics: AggregatedMetrics): string {\n let output = `## Summary Metrics\\n\\n`;\n\n // Overall\n if (metrics.overall) {\n const o = metrics.overall;\n output += `### Overall Scores\\n`;\n if (o.avgContinueReading) output += `- Continue Reading Intent: ${o.avgContinueReading.toFixed(1)}/10\\n`;\n if (o.avgRecommendation) output += `- Recommendation Score: ${o.avgRecommendation.toFixed(1)}/10\\n`;\n if (o.avgWantMore) output += `- Want More: ${o.avgWantMore.toFixed(1)}/10\\n`;\n if (o.dnfRisk) output += `- DNF Risk: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (metrics.kindleRating) {\n output += `### Predicted Kindle Rating\\n`;\n output += `- Average Score: ⭐ ${metrics.kindleRating.avgScore.toFixed(1)}/5\\n`;\n if (metrics.kindleRating.individualScores) {\n const scores = metrics.kindleRating.individualScores\n .map(s => `${s.personaName || 'Reader'}: ${s.score}/5 (${s.confidence})`)\n .join(', ');\n output += `- Individual Scores: ${scores}\\n`;\n }\n output += '\\n';\n }\n\n // Characters\n if (metrics.characters) {\n const c = metrics.characters;\n output += `### Character Evaluation\\n`;\n if (c.avgProtagonistLikability) output += `- Protagonist Likability: ${c.avgProtagonistLikability.toFixed(1)}/10\\n`;\n if (c.avgMotivationClarity) output += `- Motivation Clarity: ${c.avgMotivationClarity.toFixed(1)}/10\\n`;\n if (c.avgDialogueNaturalness) output += `- Dialogue Naturalness: ${c.avgDialogueNaturalness.toFixed(1)}/10\\n`;\n output += '\\n';\n }\n\n // Pacing\n if (metrics.pacing) {\n const p = metrics.pacing;\n output += `### Pacing Evaluation\\n`;\n if (p.avgOverall) output += `- Overall Pacing: ${p.avgOverall.toFixed(1)}/10\\n`;\n if (p.consensus) output += `- Consensus: ${p.consensus}\\n`;\n if (p.commonDnfTriggers && p.commonDnfTriggers.length > 0) {\n output += `- Common DNF Triggers:\\n`;\n p.commonDnfTriggers.slice(0, 3).forEach(t => {\n output += ` - ${t.trigger} (${t.count}x)\\n`;\n });\n }\n output += '\\n';\n }\n\n // Market\n if (metrics.market) {\n const m = metrics.market;\n output += `### Market Positioning\\n`;\n if (m.avgWordOfMouth) output += `- Word of Mouth Potential: ${m.avgWordOfMouth.toFixed(1)}/10\\n`;\n if (m.topComparableBooks && m.topComparableBooks.length > 0) {\n const books = m.topComparableBooks.slice(0, 3).map(b => b.title).join(', ');\n output += `- Comparable Books: ${books}\\n`;\n }\n output += '\\n';\n }\n\n return output;\n}\n\n/**\n * Format individual feedback\n */\nfunction formatIndividualFeedback(feedback: IndividualFeedback): string {\n const name = feedback.personaName || feedback.personaToken;\n let output = `### ${name}\\n\\n`;\n\n // Overall scores\n if (feedback.overall) {\n const o = feedback.overall;\n if (o.continueReading) output += `**Continue Reading Intent**: ${o.continueReading}/10\\n`;\n if (o.recommendation) output += `**Recommendation**: ${o.recommendation}/10\\n`;\n if (o.dnfRisk) output += `**DNF Risk**: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (feedback.kindleRating) {\n output += `**Kindle Rating**: ⭐ ${feedback.kindleRating.score}/5`;\n if (feedback.kindleRating.rationale) {\n output += ` - ${feedback.kindleRating.rationale}`;\n }\n output += '\\n\\n';\n }\n\n // Detailed Feedback\n if (feedback.detailedFeedback) {\n const d = feedback.detailedFeedback;\n\n if (d.whatWorked) {\n output += `**What Worked**:\\n${d.whatWorked}\\n\\n`;\n }\n\n if (d.whatDidntWork) {\n output += `**What Didn't Work**:\\n${d.whatDidntWork}\\n\\n`;\n }\n\n if (d.strongestElement) {\n output += `**Strongest Element**: ${d.strongestElement}\\n`;\n }\n\n if (d.weakestElement) {\n output += `**Weakest Element**: ${d.weakestElement}\\n`;\n }\n\n if (d.wouldYouBuy) {\n output += `**Would You Buy**: ${d.wouldYouBuy}\\n`;\n }\n\n if (d.specificSuggestions && d.specificSuggestions.length > 0) {\n output += `\\n**Specific Suggestions**:\\n`;\n d.specificSuggestions.forEach((s, i) => {\n output += `${i + 1}. ${s}\\n`;\n });\n }\n\n if (d.finalThoughts) {\n output += `\\n**Final Thoughts**:\\n${d.finalThoughts}\\n`;\n }\n }\n\n // Emotional Journey\n if (feedback.emotionalJourney) {\n const e = feedback.emotionalJourney;\n if (e.emotionalArc) {\n output += `\\n**Emotional Arc**: ${e.emotionalArc}\\n`;\n }\n if (e.peakMoments && e.peakMoments.length > 0) {\n const moments = e.peakMoments.map(m =>\n typeof m === 'string' ? m : (m as { moment?: string; description?: string }).moment || (m as { moment?: string; description?: string }).description || JSON.stringify(m)\n );\n output += `**Peak Moments**: ${moments.join(', ')}\\n`;\n }\n if (e.overallMood) {\n output += `**Overall Mood**: ${e.overallMood}\\n`;\n }\n }\n\n return output;\n}\n\n/**\n * Find file in snapshot\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n let result = search(files, pathParts);\n if (result) return result;\n\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n","/**\n * MCP File Operations Tools\n *\n * Provides tools for AI to read, write, create, delete, and search files in books.\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\n\n/**\n * Register file operation tools\n */\nexport function registerFileTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === read_file ===\n server.tool(\n 'read_file',\n 'Read the content of any file in a book by its path',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\" or \"chapter-01.md\")'),\n },\n async ({ book_token, path }) => {\n try {\n const result = await client.readFile(book_token, path);\n\n const fileInfo = [\n `# ${result.file.name}`,\n '',\n `**Path:** ${result.file.path}`,\n `**Words:** ${result.file.wordCount}`,\n `**Type:** ${result.file.fileType || 'unknown'}`,\n '',\n '---',\n '',\n result.content || '(empty file)',\n ].join('\\n');\n\n return {\n content: [{ type: 'text', text: fileInfo }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === write_file ===\n server.tool(\n 'write_file',\n 'Replace the entire content of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n content: z.string().describe('New content to write'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.updateFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File updated successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === edit_file ===\n server.tool(\n 'edit_file',\n 'Edit a file by replacing a specific text segment (search and replace). Use this for precise edits instead of rewriting the entire file.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n old_string: z.string().describe('The exact text to find and replace (must match exactly, including whitespace)'),\n new_string: z.string().describe('The new text to replace it with'),\n replace_all: z.boolean().optional().describe('Replace all occurrences (default: false, replaces only first match)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, old_string, new_string, replace_all, commit_message }) => {\n try {\n // 1. Read current content\n const fileData = await client.readFile(book_token, path);\n const currentContent = fileData.content || '';\n\n // 2. Check if old_string exists\n if (!currentContent.includes(old_string)) {\n return {\n content: [\n {\n type: 'text',\n text: `❌ Edit failed: Could not find the specified text in the file.\\n\\n**Looking for:**\\n\\`\\`\\`\\n${old_string.slice(0, 200)}${old_string.length > 200 ? '...' : ''}\\n\\`\\`\\`\\n\\n**Tip:** Make sure the text matches exactly, including whitespace and line breaks.`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. Check for uniqueness (if not replace_all)\n if (!replace_all) {\n const occurrences = currentContent.split(old_string).length - 1;\n if (occurrences > 1) {\n return {\n content: [\n {\n type: 'text',\n text: `❌ Edit failed: Found ${occurrences} occurrences of the text. Please provide a more specific/longer text segment to ensure a unique match, or set replace_all: true to replace all occurrences.`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // 4. Perform replacement\n const newContent = replace_all\n ? currentContent.split(old_string).join(new_string)\n : currentContent.replace(old_string, new_string);\n\n // 5. Write back\n const result = await client.updateFile(book_token, {\n path,\n content: newContent,\n commitMessage: commit_message || `Edit ${path}`,\n });\n\n const replacementCount = replace_all\n ? currentContent.split(old_string).length - 1\n : 1;\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ File edited successfully.\\n\\n**Replacements:** ${replacementCount}\\n**Commit:** ${result.commit.token}\\n**Message:** ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === create_file ===\n server.tool(\n 'create_file',\n 'Create a new file in a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path including parent folders (e.g., \"characters/new-character.md\")'),\n content: z.string().optional().describe('Initial content (optional, defaults to empty)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.createFile(book_token, {\n path,\n content: content || '',\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File created successfully.\\n\\nFile Token: ${result.fileToken}\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === delete_file ===\n server.tool(\n 'delete_file',\n 'Delete a file from a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path to delete (e.g., \"old-notes.md\")'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, commit_message }) => {\n try {\n const result = await client.deleteFile(book_token, {\n path,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File deleted successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === append_to_file ===\n server.tool(\n 'append_to_file',\n 'Append content to the end of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"notes.md\")'),\n content: z.string().describe('Content to append'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.appendToFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `Content appended successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === search_content ===\n server.tool(\n 'search_content',\n 'Search for content across all files in a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n query: z.string().describe('Search query (e.g., \"blue eyes\" or \"protagonist name\")'),\n file_types: z.array(z.string()).optional().describe('Filter by file types (e.g., [\"markdown\", \"txt\"])'),\n limit: z.number().optional().describe('Maximum number of matches to return (default: 20)'),\n },\n async ({ book_token, query, file_types, limit }) => {\n try {\n const result = await client.searchFiles(book_token, {\n query,\n fileTypes: file_types,\n limit,\n });\n\n if (result.matches.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No matches found for \"${query}\".`,\n },\n ],\n };\n }\n\n const output: string[] = [\n `# Search Results for \"${query}\"`,\n '',\n `Found ${result.matches.length} file(s) with matches:`,\n '',\n ];\n\n for (const match of result.matches) {\n output.push(`## ${match.file.path}`);\n output.push(`Words: ${match.file.wordCount} | Matches: ${match.matchCount}`);\n output.push('');\n\n for (const snippet of match.snippets) {\n output.push(`> ${snippet.text}`);\n output.push('');\n }\n }\n\n return {\n content: [{ type: 'text', text: output.join('\\n') }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n","/**\n * Logger 工具\n *\n * 重要:MCP STDIO 模式下,stdout 是協議通道,\n * 所有日誌必須輸出到 stderr\n */\n\nconst isDebug = process.env.DEBUG === 'true' || process.env.DEBUG === '1';\n\nexport const logger = {\n info: (message: string, ...args: unknown[]) => {\n console.error(`[INFO] ${message}`, ...args);\n },\n\n warn: (message: string, ...args: unknown[]) => {\n console.error(`[WARN] ${message}`, ...args);\n },\n\n error: (message: string, error?: unknown) => {\n if (error instanceof Error) {\n console.error(`[ERROR] ${message}:`, error.message);\n if (isDebug && error.stack) {\n console.error(error.stack);\n }\n } else if (error !== undefined) {\n console.error(`[ERROR] ${message}:`, error);\n } else {\n console.error(`[ERROR] ${message}`);\n }\n },\n\n debug: (message: string, ...args: unknown[]) => {\n if (isDebug) {\n console.error(`[DEBUG] ${message}`, ...args);\n }\n },\n};\n","/**\n * Token Storage Utility (CLI)\n *\n * Stores API Token in user's home directory\n * ~/.slima/credentials.json\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst SLIMA_DIR = '.slima';\nconst CREDENTIALS_FILE = 'credentials.json';\n\ninterface Credentials {\n apiToken: string;\n createdAt: string;\n baseUrl?: string;\n}\n\nfunction getCredentialsPath(): string {\n return join(homedir(), SLIMA_DIR, CREDENTIALS_FILE);\n}\n\nfunction getSlimaDir(): string {\n return join(homedir(), SLIMA_DIR);\n}\n\n/**\n * Save API Token\n */\nexport async function saveToken(apiToken: string, baseUrl?: string): Promise<void> {\n const slimaDir = getSlimaDir();\n\n // Ensure directory exists\n await fs.mkdir(slimaDir, { recursive: true });\n\n const credentials: Credentials = {\n apiToken,\n createdAt: new Date().toISOString(),\n ...(baseUrl && { baseUrl }),\n };\n\n const credentialsPath = getCredentialsPath();\n await fs.writeFile(credentialsPath, JSON.stringify(credentials, null, 2), 'utf-8');\n\n // Set file permission to 600 (owner only)\n await fs.chmod(credentialsPath, 0o600);\n}\n\n/**\n * Load API Token\n */\nexport async function loadToken(): Promise<Credentials | null> {\n try {\n const credentialsPath = getCredentialsPath();\n const content = await fs.readFile(credentialsPath, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch {\n return null;\n }\n}\n\n/**\n * Delete API Token\n */\nexport async function deleteToken(): Promise<void> {\n try {\n const credentialsPath = getCredentialsPath();\n await fs.unlink(credentialsPath);\n } catch {\n // File doesn't exist, ignore\n }\n}\n\n/**\n * Check if authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const credentials = await loadToken();\n return credentials !== null && !!credentials.apiToken;\n}\n\n/**\n * Get credentials file path (for display)\n */\nexport function getCredentialsFilePath(): string {\n return getCredentialsPath();\n}\n","/**\n * CLI Auth Command\n *\n * 實作類似 Claude Code / GitHub CLI 的認證流程:\n * 1. 使用者執行 `slima-mcp auth`\n * 2. 開啟瀏覽器到 Slima 認證頁面\n * 3. 使用者登入並授權\n * 4. 本地伺服器接收 callback 並儲存 token\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport { randomBytes } from 'node:crypto';\nimport { saveToken, loadToken, deleteToken, getCredentialsFilePath } from './token-storage.js';\n\n// 認證使用 API 伺服器(處理 OAuth 和 session)\nconst DEFAULT_API_URL = 'https://api.slima.ai';\nconst CALLBACK_PORT_START = 8765;\nconst CALLBACK_PORT_END = 8775;\n\n/**\n * 開啟瀏覽器\n */\nasync function openBrowser(url: string): Promise<void> {\n const { exec } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const execAsync = promisify(exec);\n\n const platform = process.platform;\n\n try {\n if (platform === 'darwin') {\n await execAsync(`open \"${url}\"`);\n } else if (platform === 'win32') {\n await execAsync(`start \"\" \"${url}\"`);\n } else {\n // Linux / WSL\n // 嘗試多個可能的命令\n const commands = ['xdg-open', 'sensible-browser', 'wslview'];\n for (const cmd of commands) {\n try {\n await execAsync(`${cmd} \"${url}\"`);\n return;\n } catch {\n // 繼續嘗試下一個\n }\n }\n // 如果都失敗,顯示 URL 讓使用者手動開啟\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n } catch {\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n}\n\n/**\n * 尋找可用的 port\n */\nasync function findAvailablePort(): Promise<number> {\n for (let port = CALLBACK_PORT_START; port <= CALLBACK_PORT_END; port++) {\n const available = await isPortAvailable(port);\n if (available) {\n return port;\n }\n }\n throw new Error(`No available ports between ${CALLBACK_PORT_START} and ${CALLBACK_PORT_END}`);\n}\n\nfunction isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once('error', () => resolve(false));\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n\n/**\n * 產生隨機 state(防止 CSRF)\n */\nfunction generateState(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * 成功頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getSuccessHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Welcome to Slima</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .success-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .success-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .subtitle {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .footer {\n font-size: 12px;\n color: #86868B;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"success-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\" />\n </svg>\n </div>\n <h1>Welcome to Slima</h1>\n <p class=\"subtitle\">Authentication successful. Your CLI is now connected.</p>\n <p class=\"hint\">You can close this window and return to your terminal.</p>\n <div class=\"divider\"></div>\n <p class=\"footer\">Slima MCP is ready to use</p>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 錯誤頁面 HTML\n */\n/**\n * HTML escape to prevent XSS\n */\nfunction escapeHtml(text: string): string {\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;',\n };\n return text.replace(/[&<>\"']/g, (char) => map[char]);\n}\n\n/**\n * 錯誤頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getErrorHtml(message: string): string {\n const safeMessage = escapeHtml(message);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Slima - Authentication Error</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .error-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .error-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .message {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .retry-link {\n display: inline-block;\n font-size: 14px;\n font-weight: 500;\n color: #1D1D1F;\n text-decoration: none;\n padding: 10px 20px;\n border: 1px solid #E5E5E5;\n border-radius: 8px;\n transition: all 0.2s ease;\n }\n .retry-link:hover {\n background: #F5F5F5;\n border-color: #D5D5D5;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"error-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </div>\n <h1>Something went wrong</h1>\n <p class=\"message\">${safeMessage}</p>\n <p class=\"hint\">Please close this window and try again from your terminal.</p>\n <div class=\"divider\"></div>\n <a href=\"javascript:window.close()\" class=\"retry-link\">Close Window</a>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 執行認證流程\n * @param options.baseUrl - API base URL (default: https://api.slima.ai)\n * @param options.force - Force re-authentication even if already authenticated\n */\nexport async function runAuth(options: { baseUrl?: string; force?: boolean } = {}): Promise<void> {\n const baseUrl = options.baseUrl || process.env.SLIMA_API_URL || DEFAULT_API_URL;\n const forceAuth = options.force || false;\n\n console.log('\\n🔐 Slima MCP Authentication\\n');\n\n // 檢查是否已經認證\n const existingCredentials = await loadToken();\n if (existingCredentials && !forceAuth) {\n console.log('You are already authenticated.');\n console.log(`Token stored at: ${getCredentialsFilePath()}`);\n console.log('\\nTo re-authenticate with a different account, run: slima-mcp auth --force\\n');\n return;\n }\n\n // 如果強制認證,先刪除舊 token\n if (existingCredentials && forceAuth) {\n await deleteToken();\n console.log('Clearing existing credentials for re-authentication...\\n');\n }\n\n const state = generateState();\n const port = await findAvailablePort();\n\n console.log('Starting local server for authentication callback...');\n\n // 建立本地伺服器接收 callback\n const tokenPromise = new Promise<string>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n const token = url.searchParams.get('token');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(error));\n server.close();\n reject(new Error(error));\n return;\n }\n\n if (returnedState !== state) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('Invalid state parameter. Please try again.'));\n server.close();\n reject(new Error('State mismatch'));\n return;\n }\n\n if (!token) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('No token received. Please try again.'));\n server.close();\n reject(new Error('No token received'));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessHtml());\n server.close();\n resolve(token);\n } else {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n console.log(`Listening on http://127.0.0.1:${port}`);\n });\n\n // 30 秒超時\n setTimeout(() => {\n server.close();\n reject(new Error('Authentication timed out. Please try again.'));\n }, 30000);\n });\n\n // 建立認證 URL(使用 API endpoint)\n // 加入 prompt=login 強制重新登入(清除 Rails session)\n const promptParam = forceAuth ? '&prompt=login' : '';\n const authUrl = `${baseUrl}/api/v1/auth/cli?state=${state}&callback_port=${port}${promptParam}`;\n\n console.log('\\nOpening browser for authentication...');\n await openBrowser(authUrl);\n\n console.log('\\nWaiting for authentication...');\n console.log('(Press Ctrl+C to cancel)\\n');\n\n try {\n const token = await tokenPromise;\n await saveToken(token, baseUrl !== DEFAULT_API_URL ? baseUrl : undefined);\n\n console.log('\\n✅ Authentication successful!');\n console.log(`Token saved to: ${getCredentialsFilePath()}`);\n console.log('\\nYou can now use Slima MCP with any MCP-compatible AI tool.\\n');\n } catch (error) {\n console.error('\\n❌ Authentication failed:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\n/**\n * 登出(刪除 token)\n */\nexport async function runLogout(): Promise<void> {\n console.log('\\n🔓 Slima MCP Logout\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('You are not authenticated.\\n');\n return;\n }\n\n await deleteToken();\n console.log('✅ Successfully logged out.');\n console.log(`Removed credentials from: ${getCredentialsFilePath()}\\n`);\n}\n\n/**\n * 顯示目前狀態\n */\nexport async function runStatus(): Promise<void> {\n console.log('\\n📊 Slima MCP Status\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('Status: Not authenticated');\n console.log('\\nRun `slima-mcp auth` to authenticate.\\n');\n return;\n }\n\n console.log('Status: Authenticated');\n console.log(`Token: ${credentials.apiToken.substring(0, 15)}...`);\n console.log(`Authenticated at: ${credentials.createdAt}`);\n if (credentials.baseUrl) {\n console.log(`API URL: ${credentials.baseUrl}`);\n }\n console.log(`Credentials file: ${getCredentialsFilePath()}\\n`);\n}\n"],"mappings":";;;AAWA,SAAS,4BAA4B;;;ACPrC,SAAS,iBAAiB;;;ACAnB,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YACS,QACA,MACP,SACA;AACA,UAAM,WAAW,cAAc,MAAM,EAAE;AAJhC;AACA;AAIP,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,cAAc;AAAA,EACrD,YAAY,UAAU,gCAAgC;AACpD,UAAM,KAAK,gBAAgB,OAAO;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,gBAAN,cAA4B,cAAc;AAAA,EAC/C,YAAY,WAAW,YAAY;AACjC,UAAM,KAAK,aAAa,GAAG,QAAQ,YAAY;AAC/C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,2BAAN,cAAuC,cAAc;AAAA,EAC1D,cAAc;AACZ,UAAM,KAAK,wBAAwB,sBAAsB;AACzD,SAAK,OAAO;AAAA,EACd;AACF;AAKO,SAAS,kBAAkB,OAAwB;AACxD,MAAI,iBAAiB,eAAe;AAClC,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,iBAAiB,OAAO;AAC1B,WAAO,MAAM;AAAA,EACf;AAEA,SAAO;AACT;;;ACnBA,IAAM,gBAAwB;AAAA,EAC5B,OAAO,MAAM;AAAA,EAAC;AAAA,EACd,MAAM,MAAM;AAAA,EAAC;AAAA,EACb,MAAM,MAAM;AAAA,EAAC;AAAA,EACb,OAAO,MAAM;AAAA,EAAC;AAChB;AAqBA,SAAS,eAAe,QAA8D;AACpF,SAAO,WAAW,UAAU,OAAQ,OAAuB,UAAU;AACvE;AAEO,IAAM,iBAAN,MAAqB;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAuC;AACjD,SAAK,UAAU,OAAO,QAAQ,QAAQ,OAAO,EAAE;AAG/C,QAAI,eAAe,MAAM,GAAG;AAC1B,YAAM,cAAc,OAAO;AAC3B,WAAK,WAAW,YAAY;AAC5B,WAAK,SAAS;AAAA,IAChB,OAAO;AACL,WAAK,WAAW,OAAO;AACvB,WAAK,SAAS,OAAO,UAAU;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,QACZ,QACA,MACA,MACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAClC,UAAM,QAAQ,MAAM,KAAK,SAAS;AAElC,SAAK,OAAO,MAAM,GAAG,MAAM,IAAI,IAAI,EAAE;AAErC,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC;AAAA,MACA,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAChB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,KAAK,oBAAoB,QAAQ;AAAA,IACzC;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBAAoB,UAAoC;AACpE,QAAI;AAEJ,QAAI;AACF,kBAAa,MAAM,SAAS,KAAK;AAAA,IACnC,QAAQ;AAAA,IAER;AAEA,UAAM,OAAO,WAAW,OAAO;AAC/B,UAAM,UAAU,WAAW,OAAO;AAElC,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK;AACH,cAAM,IAAI,oBAAoB,OAAO;AAAA,MACvC,KAAK;AACH,cAAM,IAAI,cAAc,OAAO;AAAA,MACjC,KAAK;AACH,cAAM,IAAI,yBAAyB;AAAA,MACrC;AACE,cAAM,IAAI,cAAc,SAAS,QAAQ,MAAM,OAAO;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAA6B;AACjC,WAAO,KAAK,QAAgB,OAAO,eAAe;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,OAA8B;AAC1C,WAAO,KAAK,QAAc,OAAO,iBAAiB,KAAK,EAAE;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,QAIC;AAChB,WAAO,KAAK,QAAc,QAAQ,iBAAiB;AAAA,MACjD,MAAM;AAAA,QACJ,OAAO,OAAO;AAAA,QACd,aAAa,OAAO;AAAA,QACpB,aAAa,OAAO;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,WAAmB,QAAQ,IAAuB;AAClE,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,MACA,iBAAiB,SAAS,kBAAkB,KAAK;AAAA,IACnD;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,WAAmB,aAAsC;AACvE,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS,YAAY,WAAW;AAAA,IACnD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,WAAsC;AACvD,WAAO,KAAK,QAAkB,OAAO,iBAAiB,SAAS,WAAW;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,WAAmB,QAAmC;AACxE,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B,EAAE,OAAO;AAAA,IACX;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAAa,OAAoC;AACrD,UAAM,SAAS,QAAQ,UAAU,mBAAmB,KAAK,CAAC,KAAK;AAC/D,WAAO,KAAK,QAAmB,OAAO,mBAAmB,MAAM,EAAE;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,OAAiC;AAChD,WAAO,KAAK,QAAiB,OAAO,oBAAoB,KAAK,EAAE;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,iBACJ,WACA,QACqB;AACrB,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B;AAAA,QACE,aAAa;AAAA,UACX,aAAa,OAAO,cAAc;AAAA,UAClC,gBAAgB,OAAO;AAAA,UACvB,cAAc,OAAO;AAAA,UACrB,OAAO,OAAO;AAAA,UACd,cAAc,OAAO;AAAA,QACvB;AAAA,QACA,SAAS,OAAO;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,WACA,WAC6B;AAC7B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS,iBAAiB,SAAS;AAAA,IACtD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,WAAmB,WAAwC;AAC7E,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS,iBAAiB,SAAS;AAAA,IACtD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAS,WAAmB,MAA4C;AAC5E,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B,EAAE,KAAK;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,WACA,QACgC;AAChC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B;AAAA,QACE,MAAM,OAAO;AAAA,QACb,SAAS,OAAO,WAAW;AAAA,QAC3B,aAAa,OAAO;AAAA,QACpB,gBAAgB,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,WACA,QACgC;AAChC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B;AAAA,QACE,MAAM,OAAO;AAAA,QACb,SAAS,OAAO;AAAA,QAChB,gBAAgB,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,WACA,QACgC;AAChC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B;AAAA,QACE,MAAM,OAAO;AAAA,QACb,gBAAgB,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,WACA,QACgC;AAChC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B;AAAA,QACE,MAAM,OAAO;AAAA,QACb,SAAS,OAAO;AAAA,QAChB,gBAAgB,OAAO;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACJ,WACA,QAC4B;AAC5B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B;AAAA,QACE,OAAO,OAAO;AAAA,QACd,YAAY,OAAO;AAAA,QACnB,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;;;ACpYA,SAAS,SAAS;AASX,SAAS,kBACd,QACA,QACM;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,MAClD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wBAAwB;AAAA,MACpE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6BAA6B;AAAA,IAC3E;AAAA,IACA,OAAO,EAAE,OAAO,aAAa,YAAY,MAAM;AAC7C,UAAI;AACF,cAAM,OAAO,MAAM,OAAO,WAAW;AAAA,UACnC;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QACF,CAAC;AAED,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA,eAAgD,KAAK,KAAK;AAAA,eAAkB,KAAK,KAAK;AAAA,gBAAmB,KAAK,cAAc,KAAK;AAAA;AAAA,2DAAgE,KAAK,KAAK;AAAA,YACnN;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY;AACV,UAAI;AACF,cAAM,QAAQ,MAAM,OAAO,UAAU;AAErC,YAAI,MAAM,WAAW,GAAG;AACtB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,cAAM,YAAY,MACf,IAAI,CAAC,SAAS;AACb,gBAAM,QAAQ,KAAK,gBAAgB,eAAe,KAAK;AACvD,iBAAO,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK;AAAA,YAAgB,KAAK,cAAc,KAAK,aAAa,KAAK;AAAA,QACrG,CAAC,EACA,KAAK,MAAM;AAEd,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,SAAS,MAAM,MAAM;AAAA;AAAA,EAAgB,SAAS;AAAA,YACtD;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAY,EAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,IAChE;AAAA,IACA,OAAO,EAAE,WAAW,MAAM;AACxB,UAAI;AACF,cAAM,OAAO,MAAM,OAAO,QAAQ,UAAU;AAE5C,cAAM,OAAO,KAAK,KAAK,KAAK;AAAA;AAAA,eAErB,KAAK,KAAK;AAAA,gBACT,KAAK,cAAc,KAAK;AAAA,qBACnB,KAAK,eAAe,KAAK;AAAA,kBAC5B,KAAK,YAAY,KAAK;AAAA,qBACnB,KAAK,gBAAgB,eAAe,KAAK,GAAG;AAAA,0BACvC,KAAK,qBAAqB,eAAe,KAAK,GAAG;AAAA,iBAC1D,KAAK,SAAS;AAAA,iBACd,KAAK,SAAS;AAEvB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,QAClC;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAY,EAAE,OAAO,EAAE,SAAS,YAAY;AAAA,IAC9C;AAAA,IACA,OAAO,EAAE,WAAW,MAAM;AACxB,UAAI;AACF,cAAM,UAAU,MAAM,OAAO,YAAY,YAAY,CAAC;AAEtD,YAAI,QAAQ,WAAW,GAAG;AACxB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,cAAM,WAAW,QAAQ,CAAC,EAAE;AAC5B,cAAM,OAAO,eAAe,QAAQ;AAEpC,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,2BAA2B,UAAU;AAAA;AAAA,EAAQ,IAAI;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAY,EAAE,OAAO,EAAE,SAAS,YAAY;AAAA,IAC9C;AAAA,IACA,OAAO,EAAE,WAAW,MAAM;AACxB,UAAI;AACF,cAAM,CAAC,MAAM,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,UACxC,OAAO,QAAQ,UAAU;AAAA,UACzB,OAAO,YAAY,YAAY,EAAE;AAAA,QACnC,CAAC;AAGD,YAAI,aAAa;AACjB,YAAI,QAAQ,SAAS,GAAG;AACtB,gBAAM,SAAS,QAAQ,CAAC,EAAE,kBAAkB;AAC5C,gBAAM,SAAS,QAAQ,QAAQ,SAAS,CAAC,EAAE,kBAAkB;AAC7D,uBAAa,SAAS;AAAA,QACxB;AAEA,cAAM,OAAO,cAAc,IAAI,MAAM;AACrC,cAAM,eACJ,QAAQ,SAAS,IACb,GAAG,IAAI,GAAG,WAAW,eAAe,CAAC,gBAAgB,QAAQ,MAAM,cACnE;AAEN,cAAM,OAAO,oBAAoB,KAAK,KAAK;AAAA;AAAA,qBAE9B,KAAK,gBAAgB,eAAe,KAAK,GAAG;AAAA,0BACvC,KAAK,qBAAqB,eAAe,KAAK,GAAG;AAAA,yBAClD,YAAY;AAAA,0BACX,QAAQ,MAAM;AAAA,kBACtB,KAAK,YAAY,KAAK;AAEhC,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,QAClC;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKA,SAAS,eAAe,OAAuB,SAAS,GAAW;AACjE,SAAO,MACJ,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,EACtC,IAAI,CAAC,MAAM;AACV,UAAM,SAAS,KAAK,OAAO,MAAM;AACjC,UAAM,OAAO,EAAE,SAAS,WAAW,cAAO;AAC1C,UAAM,QAAQ,EAAE,SAAS,UAAU,EAAE,YAAY,KAAK,EAAE,SAAS,YAAY;AAC7E,UAAM,aAAa,EAAE,eAAe,SAAS;AAC7C,QAAI,OAAO,GAAG,MAAM,GAAG,IAAI,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,UAAU;AAE1D,QAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,cAAQ,OAAO,eAAe,EAAE,UAAU,SAAS,CAAC;AAAA,IACtD;AAEA,WAAO;AAAA,EACT,CAAC,EACA,KAAK,IAAI;AACd;;;AC5OA,SAAS,KAAAA,UAAS;AASX,SAAS,qBACd,QACA,QACM;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYC,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,WAAWA,GAAE,OAAO,EAAE,SAAS,iDAAiD;AAAA,IAClF;AAAA,IACA,OAAO,EAAE,YAAY,UAAU,MAAM;AACnC,UAAI;AAEF,cAAM,UAAU,MAAM,OAAO,YAAY,YAAY,CAAC;AAEtD,YAAI,QAAQ,WAAW,GAAG;AACxB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,OAAO,mBAAmB,QAAQ,CAAC,EAAE,eAAe,SAAS;AAEnE,YAAI,CAAC,MAAM;AAET,gBAAM,iBAAiB,aAAa,QAAQ,CAAC,EAAE,aAAa;AAC5D,gBAAM,WAAW,eAAe,MAAM,GAAG,EAAE,EAAE,KAAK,MAAM;AACxD,gBAAM,WACJ,eAAe,SAAS,KACpB;AAAA,UAAa,eAAe,SAAS,EAAE,gBACvC;AAEN,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,mBAAmB,SAAS;AAAA;AAAA;AAAA,IAA2B,QAAQ,GAAG,QAAQ;AAAA;AAAA;AAAA,cAClF;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI,KAAK,SAAS,UAAU;AAC1B,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,IAAI,SAAS;AAAA,cACrB;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI,CAAC,KAAK,UAAU;AAClB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,SAAS,SAAS;AAAA,cAC1B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,QAAQ,MAAM,OAAO,cAAc,YAAY,CAAC,KAAK,QAAQ,CAAC;AAEpE,YAAI,MAAM,WAAW,GAAG;AACtB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,cAAM,UAAU,MAAM,CAAC,EAAE;AACzB,cAAM,WAAW,KAAK,YAAY,KAAK,KAAK,SAAS,YAAY;AAEjE,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,KAAK,KAAK,IAAI,GAAG,QAAQ;AAAA;AAAA,EAAO,OAAO;AAAA,YAC/C;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAUA,SAAS,mBACP,OACA,MACqB;AAErB,QAAM,iBAAiB,KAAK,QAAQ,OAAO,EAAE,EAAE,YAAY;AAC3D,QAAM,YAAY,eAAe,MAAM,GAAG;AAG1C,WAAS,OACP,OACA,gBACqB;AACrB,eAAW,QAAQ,OAAO;AACxB,YAAM,gBAAgB,KAAK,KAAK,YAAY;AAC5C,YAAM,iBAAiB,KAAK,MAAM,YAAY;AAG9C,UAAI,eAAe,WAAW,GAAG;AAC/B,cAAM,SAAS,eAAe,CAAC;AAC/B,YACE,kBAAkB,UAClB,mBAAmB,UACnB,cAAc,QAAQ,YAAY,EAAE,MAAM,OAAO,QAAQ,YAAY,EAAE,GACvE;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAGA,UAAI,KAAK,SAAS,YAAY,KAAK,UAAU;AAC3C,YACE,kBAAkB,eAAe,CAAC,KAClC,mBAAmB,eAAe,CAAC,GACnC;AACA,gBAAMC,UAAS,OAAO,KAAK,UAAU,eAAe,MAAM,CAAC,CAAC;AAC5D,cAAIA,QAAQ,QAAOA;AAAA,QACrB;AAGA,cAAMA,UAAS,OAAO,KAAK,UAAU,cAAc;AACnD,YAAIA,QAAQ,QAAOA;AAAA,MACrB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,OAAO,OAAO,SAAS;AACpC,MAAI,OAAQ,QAAO;AAGnB,WAAS,aAAa,OAAO,UAAU,UAAU,SAAS,CAAC,CAAC;AAC5D,SAAO;AACT;AAKA,SAAS,aACP,OACA,MACqB;AACrB,QAAM,YAAY,KAAK,YAAY;AAEnC,aAAW,QAAQ,OAAO;AACxB,UAAM,gBAAgB,KAAK,KAAK,YAAY;AAC5C,UAAM,iBAAiB,KAAK,MAAM,YAAY;AAE9C,QACE,kBAAkB,aAClB,mBAAmB,aACnB,cAAc,QAAQ,YAAY,EAAE,MAAM,UAAU,QAAQ,YAAY,EAAE,GAC1E;AACA,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,SAAS,YAAY,KAAK,UAAU;AAC3C,YAAM,SAAS,aAAa,KAAK,UAAU,IAAI;AAC/C,UAAI,OAAQ,QAAO;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,aAAa,OAAuB,SAAS,IAAc;AAClE,QAAM,SAAmB,CAAC;AAE1B,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,SAAS,GAAG,MAAM,IAAI,KAAK,IAAI,KAAK,KAAK;AAEtD,QAAI,KAAK,SAAS,QAAQ;AACxB,aAAO,KAAK,IAAI;AAAA,IAClB,WAAW,KAAK,UAAU;AACxB,aAAO,KAAK,GAAG,aAAa,KAAK,UAAU,IAAI,CAAC;AAAA,IAClD;AAAA,EACF;AAEA,SAAO;AACT;;;ACvOA,SAAS,KAAAC,UAAS;AAOlB,IAAMC,iBAAwB;AAAA,EAC5B,OAAO,MAAM;AAAA,EAAC;AAAA,EACd,MAAM,MAAM;AAAA,EAAC;AAAA,EACb,MAAM,MAAM;AAAA,EAAC;AAAA,EACb,OAAO,MAAM;AAAA,EAAC;AAChB;AAKO,SAAS,wBACd,QACA,QACAC,UAAiBD,gBACX;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAOE,GACJ,OAAO,EACP,SAAS,EACT,SAAS,iDAAiD;AAAA,IAC/D;AAAA,IACA,OAAO,EAAE,MAAM,MAAM;AACnB,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,aAAa,KAAK;AAEhD,YAAI,SAAS,WAAW,GAAG;AACzB,gBAAM,YAAY,QAAQ,eAAe,KAAK,MAAM;AACpD,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,oBAAoB,SAAS;AAAA,cACrC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,cAAM,YAAY,SACf,IAAI,CAAC,MAAM;AACV,gBAAM,OAAO,EAAE,eAAe,MAAM,EAAE,eAAe,MAAM,EAAE;AAC7D,gBAAM,OAAO,EAAE,WAAW,KAAK,IAAI,KAAK;AACxC,gBAAM,OAAO,EAAE,gBAAgB,CAAC;AAChC,gBAAM,SAAS,KAAK,UAAU;AAC9B,gBAAM,MAAM,KAAK,YAAY;AAE7B,iBAAO,OAAO,IAAI,SAAS,EAAE,KAAK;AAAA,YAClC,MAAM,WAAW,GAAG;AAAA,YACpB,IAAI;AAAA,QACN,CAAC,EACA,KAAK,MAAM;AAEd,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,uBAAuB,SAAS,MAAM;AAAA;AAAA,EAAS,SAAS;AAAA;AAAA;AAAA,YAChE;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,WAAWA,GAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MAC1E,eAAeA,GACZ,OAAO,EACP,SAAS,8EAA8E;AAAA,IAC5F;AAAA,IACA,OAAO,EAAE,YAAY,WAAW,cAAc,MAAM;AAClD,UAAI;AAEF,cAAM,UAAU,MAAM,OAAO,YAAY,YAAY,CAAC;AAEtD,YAAI,QAAQ,WAAW,GAAG;AACxB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAGA,cAAM,OAAOC,oBAAmB,QAAQ,CAAC,EAAE,eAAe,SAAS;AAEnE,YAAI,CAAC,MAAM;AACT,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,mBAAmB,SAAS;AAAA,cACpC;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI,CAAC,KAAK,UAAU;AAClB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,SAAS,SAAS;AAAA,cAC1B;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAGA,cAAM,QAAQ,MAAM,OAAO,cAAc,YAAY,CAAC,KAAK,QAAQ,CAAC;AAEpE,YAAI,MAAM,WAAW,GAAG;AACtB,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,cAAM,UAAU,MAAM,CAAC,EAAE;AAGzB,QAAAF,QAAO,KAAK,4BAA4B,SAAS,iBAAiB,aAAa,EAAE;AAEjF,cAAM,OAAO,MAAM,OAAO,iBAAiB,YAAY;AAAA,UACrD,YAAY;AAAA,UACZ,eAAe,CAAC,aAAa;AAAA,UAC7B,aAAa,QAAQ,CAAC,EAAE;AAAA,UACxB,aAAa,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,UACrC;AAAA,QACF,CAAC;AAGD,cAAM,SAAS,MAAM;AAAA,UACnB,MAAM,OAAO,sBAAsB,YAAY,KAAK,KAAK;AAAA,UACzD;AAAA,UACAA;AAAA,QACF;AAEA,YAAI,OAAO,WAAW,UAAU;AAC9B,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAEA,YAAI,OAAO,WAAW,aAAa;AACjC,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA,WAA8E,KAAK,KAAK;AAAA,cAChG;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,aAAa,MAAM,OAAO,cAAc,YAAY,KAAK,KAAK;AAGpE,cAAM,WAAW,yBAAyB,YAAY,KAAK,IAAI;AAE/D,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,SAAS,CAAC;AAAA,QAC5C;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKA,eAAe,kBACb,SACA,WACAA,SAC6B;AAC7B,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,eAAe;AAErB,SAAO,KAAK,IAAI,IAAI,YAAY,WAAW;AACzC,UAAM,SAAS,MAAM,QAAQ;AAC7B,IAAAA,QAAO,MAAM,gBAAgB,OAAO,MAAM,eAAe,OAAO,YAAY,KAAK,EAAE;AAEnF,QAAI,OAAO,WAAW,eAAe,OAAO,WAAW,UAAU;AAC/D,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,YAAY;AAAA,EAC1B;AAEA,SAAO,EAAE,QAAQ,UAAU;AAC7B;AAKA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAKA,SAAS,yBAAyB,MAAkB,UAA0B;AAC5E,QAAM,YAAY,KAAK,uBAAuB,CAAC;AAC/C,QAAM,UAAU,KAAK;AAErB,MAAI,UAAU,WAAW,KAAK,CAAC,SAAS;AACtC,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,2BAA2B,QAAQ;AAAA;AAAA;AAGhD,MAAI,SAAS;AACX,cAAU,wBAAwB,OAAO;AAAA,EAC3C;AAGA,MAAI,UAAU,SAAS,GAAG;AACxB,cAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AACV,cAAU,UAAU,IAAI,wBAAwB,EAAE,KAAK,WAAW;AAAA,EACpE;AAEA,SAAO;AACT;AAKA,SAAS,wBAAwB,SAAoC;AACnE,MAAI,SAAS;AAAA;AAAA;AAGb,MAAI,QAAQ,SAAS;AACnB,UAAM,IAAI,QAAQ;AAClB,cAAU;AAAA;AACV,QAAI,EAAE,mBAAoB,WAAU,8BAA8B,EAAE,mBAAmB,QAAQ,CAAC,CAAC;AAAA;AACjG,QAAI,EAAE,kBAAmB,WAAU,2BAA2B,EAAE,kBAAkB,QAAQ,CAAC,CAAC;AAAA;AAC5F,QAAI,EAAE,YAAa,WAAU,gBAAgB,EAAE,YAAY,QAAQ,CAAC,CAAC;AAAA;AACrE,QAAI,EAAE,QAAS,WAAU,eAAe,EAAE,OAAO;AAAA;AACjD,cAAU;AAAA,EACZ;AAGA,MAAI,QAAQ,cAAc;AACxB,cAAU;AAAA;AACV,cAAU,2BAAsB,QAAQ,aAAa,SAAS,QAAQ,CAAC,CAAC;AAAA;AACxE,QAAI,QAAQ,aAAa,kBAAkB;AACzC,YAAM,SAAS,QAAQ,aAAa,iBACjC,IAAI,OAAK,GAAG,EAAE,eAAe,QAAQ,KAAK,EAAE,KAAK,OAAO,EAAE,UAAU,GAAG,EACvE,KAAK,IAAI;AACZ,gBAAU,wBAAwB,MAAM;AAAA;AAAA,IAC1C;AACA,cAAU;AAAA,EACZ;AAGA,MAAI,QAAQ,YAAY;AACtB,UAAM,IAAI,QAAQ;AAClB,cAAU;AAAA;AACV,QAAI,EAAE,yBAA0B,WAAU,6BAA6B,EAAE,yBAAyB,QAAQ,CAAC,CAAC;AAAA;AAC5G,QAAI,EAAE,qBAAsB,WAAU,yBAAyB,EAAE,qBAAqB,QAAQ,CAAC,CAAC;AAAA;AAChG,QAAI,EAAE,uBAAwB,WAAU,2BAA2B,EAAE,uBAAuB,QAAQ,CAAC,CAAC;AAAA;AACtG,cAAU;AAAA,EACZ;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI,QAAQ;AAClB,cAAU;AAAA;AACV,QAAI,EAAE,WAAY,WAAU,qBAAqB,EAAE,WAAW,QAAQ,CAAC,CAAC;AAAA;AACxE,QAAI,EAAE,UAAW,WAAU,gBAAgB,EAAE,SAAS;AAAA;AACtD,QAAI,EAAE,qBAAqB,EAAE,kBAAkB,SAAS,GAAG;AACzD,gBAAU;AAAA;AACV,QAAE,kBAAkB,MAAM,GAAG,CAAC,EAAE,QAAQ,OAAK;AAC3C,kBAAU,OAAO,EAAE,OAAO,KAAK,EAAE,KAAK;AAAA;AAAA,MACxC,CAAC;AAAA,IACH;AACA,cAAU;AAAA,EACZ;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI,QAAQ;AAClB,cAAU;AAAA;AACV,QAAI,EAAE,eAAgB,WAAU,8BAA8B,EAAE,eAAe,QAAQ,CAAC,CAAC;AAAA;AACzF,QAAI,EAAE,sBAAsB,EAAE,mBAAmB,SAAS,GAAG;AAC3D,YAAM,QAAQ,EAAE,mBAAmB,MAAM,GAAG,CAAC,EAAE,IAAI,OAAK,EAAE,KAAK,EAAE,KAAK,IAAI;AAC1E,gBAAU,uBAAuB,KAAK;AAAA;AAAA,IACxC;AACA,cAAU;AAAA,EACZ;AAEA,SAAO;AACT;AAKA,SAAS,yBAAyB,UAAsC;AACtE,QAAM,OAAO,SAAS,eAAe,SAAS;AAC9C,MAAI,SAAS,OAAO,IAAI;AAAA;AAAA;AAGxB,MAAI,SAAS,SAAS;AACpB,UAAM,IAAI,SAAS;AACnB,QAAI,EAAE,gBAAiB,WAAU,gCAAgC,EAAE,eAAe;AAAA;AAClF,QAAI,EAAE,eAAgB,WAAU,uBAAuB,EAAE,cAAc;AAAA;AACvE,QAAI,EAAE,QAAS,WAAU,iBAAiB,EAAE,OAAO;AAAA;AACnD,cAAU;AAAA,EACZ;AAGA,MAAI,SAAS,cAAc;AACzB,cAAU,6BAAwB,SAAS,aAAa,KAAK;AAC7D,QAAI,SAAS,aAAa,WAAW;AACnC,gBAAU,MAAM,SAAS,aAAa,SAAS;AAAA,IACjD;AACA,cAAU;AAAA,EACZ;AAGA,MAAI,SAAS,kBAAkB;AAC7B,UAAM,IAAI,SAAS;AAEnB,QAAI,EAAE,YAAY;AAChB,gBAAU;AAAA,EAAqB,EAAE,UAAU;AAAA;AAAA;AAAA,IAC7C;AAEA,QAAI,EAAE,eAAe;AACnB,gBAAU;AAAA,EAA0B,EAAE,aAAa;AAAA;AAAA;AAAA,IACrD;AAEA,QAAI,EAAE,kBAAkB;AACtB,gBAAU,0BAA0B,EAAE,gBAAgB;AAAA;AAAA,IACxD;AAEA,QAAI,EAAE,gBAAgB;AACpB,gBAAU,wBAAwB,EAAE,cAAc;AAAA;AAAA,IACpD;AAEA,QAAI,EAAE,aAAa;AACjB,gBAAU,sBAAsB,EAAE,WAAW;AAAA;AAAA,IAC/C;AAEA,QAAI,EAAE,uBAAuB,EAAE,oBAAoB,SAAS,GAAG;AAC7D,gBAAU;AAAA;AAAA;AACV,QAAE,oBAAoB,QAAQ,CAAC,GAAG,MAAM;AACtC,kBAAU,GAAG,IAAI,CAAC,KAAK,CAAC;AAAA;AAAA,MAC1B,CAAC;AAAA,IACH;AAEA,QAAI,EAAE,eAAe;AACnB,gBAAU;AAAA;AAAA,EAA0B,EAAE,aAAa;AAAA;AAAA,IACrD;AAAA,EACF;AAGA,MAAI,SAAS,kBAAkB;AAC7B,UAAM,IAAI,SAAS;AACnB,QAAI,EAAE,cAAc;AAClB,gBAAU;AAAA,qBAAwB,EAAE,YAAY;AAAA;AAAA,IAClD;AACA,QAAI,EAAE,eAAe,EAAE,YAAY,SAAS,GAAG;AAC7C,YAAM,UAAU,EAAE,YAAY;AAAA,QAAI,OAChC,OAAO,MAAM,WAAW,IAAK,EAAgD,UAAW,EAAgD,eAAe,KAAK,UAAU,CAAC;AAAA,MACzK;AACA,gBAAU,qBAAqB,QAAQ,KAAK,IAAI,CAAC;AAAA;AAAA,IACnD;AACA,QAAI,EAAE,aAAa;AACjB,gBAAU,qBAAqB,EAAE,WAAW;AAAA;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAASE,oBACP,OACA,MACqB;AACrB,QAAM,iBAAiB,KAAK,QAAQ,OAAO,EAAE,EAAE,YAAY;AAC3D,QAAM,YAAY,eAAe,MAAM,GAAG;AAE1C,WAAS,OACP,OACA,gBACqB;AACrB,eAAW,QAAQ,OAAO;AACxB,YAAM,gBAAgB,KAAK,KAAK,YAAY;AAC5C,YAAM,iBAAiB,KAAK,MAAM,YAAY;AAE9C,UAAI,eAAe,WAAW,GAAG;AAC/B,cAAM,SAAS,eAAe,CAAC;AAC/B,YACE,kBAAkB,UAClB,mBAAmB,UACnB,cAAc,QAAQ,YAAY,EAAE,MAAM,OAAO,QAAQ,YAAY,EAAE,GACvE;AACA,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,YAAY,KAAK,UAAU;AAC3C,YACE,kBAAkB,eAAe,CAAC,KAClC,mBAAmB,eAAe,CAAC,GACnC;AACA,gBAAMC,UAAS,OAAO,KAAK,UAAU,eAAe,MAAM,CAAC,CAAC;AAC5D,cAAIA,QAAQ,QAAOA;AAAA,QACrB;AAEA,cAAMA,UAAS,OAAO,KAAK,UAAU,cAAc;AACnD,YAAIA,QAAQ,QAAOA;AAAA,MACrB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,OAAO,OAAO,SAAS;AACpC,MAAI,OAAQ,QAAO;AAEnB,WAASC,cAAa,OAAO,UAAU,UAAU,SAAS,CAAC,CAAC;AAC5D,SAAO;AACT;AAEA,SAASA,cACP,OACA,MACqB;AACrB,QAAM,YAAY,KAAK,YAAY;AAEnC,aAAW,QAAQ,OAAO;AACxB,UAAM,gBAAgB,KAAK,KAAK,YAAY;AAC5C,UAAM,iBAAiB,KAAK,MAAM,YAAY;AAE9C,QACE,kBAAkB,aAClB,mBAAmB,aACnB,cAAc,QAAQ,YAAY,EAAE,MAAM,UAAU,QAAQ,YAAY,EAAE,GAC1E;AACA,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,SAAS,YAAY,KAAK,UAAU;AAC3C,YAAM,SAASA,cAAa,KAAK,UAAU,IAAI;AAC/C,UAAI,OAAQ,QAAO;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;;;ACjfA,SAAS,KAAAC,UAAS;AAQX,SAAS,kBACd,QACA,QACM;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYC,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,MAAMA,GAAE,OAAO,EAAE,SAAS,kEAAkE;AAAA,IAC9F;AAAA,IACA,OAAO,EAAE,YAAY,KAAK,MAAM;AAC9B,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,SAAS,YAAY,IAAI;AAErD,cAAM,WAAW;AAAA,UACf,KAAK,OAAO,KAAK,IAAI;AAAA,UACrB;AAAA,UACA,aAAa,OAAO,KAAK,IAAI;AAAA,UAC7B,cAAc,OAAO,KAAK,SAAS;AAAA,UACnC,aAAa,OAAO,KAAK,YAAY,SAAS;AAAA,UAC9C;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,WAAW;AAAA,QACpB,EAAE,KAAK,IAAI;AAEX,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,SAAS,CAAC;AAAA,QAC5C;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,MAAMA,GAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,MACzE,SAASA,GAAE,OAAO,EAAE,SAAS,sBAAsB;AAAA,MACnD,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2DAA2D;AAAA,IAC5G;AAAA,IACA,OAAO,EAAE,YAAY,MAAM,SAAS,eAAe,MAAM;AACvD,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,WAAW,YAAY;AAAA,UACjD;AAAA,UACA;AAAA,UACA,eAAe;AAAA,QACjB,CAAC;AAED,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA,UAAyC,OAAO,OAAO,KAAK;AAAA,WAAc,OAAO,OAAO,QAAQ,OAAO,OAAO,OAAO;AAAA,YAC7H;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,MAAMA,GAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,MACzE,YAAYA,GAAE,OAAO,EAAE,SAAS,+EAA+E;AAAA,MAC/G,YAAYA,GAAE,OAAO,EAAE,SAAS,iCAAiC;AAAA,MACjE,aAAaA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,qEAAqE;AAAA,MAClH,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2DAA2D;AAAA,IAC5G;AAAA,IACA,OAAO,EAAE,YAAY,MAAM,YAAY,YAAY,aAAa,eAAe,MAAM;AACnF,UAAI;AAEF,cAAM,WAAW,MAAM,OAAO,SAAS,YAAY,IAAI;AACvD,cAAM,iBAAiB,SAAS,WAAW;AAG3C,YAAI,CAAC,eAAe,SAAS,UAAU,GAAG;AACxC,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA,EAA8F,WAAW,MAAM,GAAG,GAAG,CAAC,GAAG,WAAW,SAAS,MAAM,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA,cACrK;AAAA,YACF;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AAGA,YAAI,CAAC,aAAa;AAChB,gBAAM,cAAc,eAAe,MAAM,UAAU,EAAE,SAAS;AAC9D,cAAI,cAAc,GAAG;AACnB,mBAAO;AAAA,cACL,SAAS;AAAA,gBACP;AAAA,kBACE,MAAM;AAAA,kBACN,MAAM,6BAAwB,WAAW;AAAA,gBAC3C;AAAA,cACF;AAAA,cACA,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAGA,cAAM,aAAa,cACf,eAAe,MAAM,UAAU,EAAE,KAAK,UAAU,IAChD,eAAe,QAAQ,YAAY,UAAU;AAGjD,cAAM,SAAS,MAAM,OAAO,WAAW,YAAY;AAAA,UACjD;AAAA,UACA,SAAS;AAAA,UACT,eAAe,kBAAkB,QAAQ,IAAI;AAAA,QAC/C,CAAC;AAED,cAAM,mBAAmB,cACrB,eAAe,MAAM,UAAU,EAAE,SAAS,IAC1C;AAEJ,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA,oBAAoD,gBAAgB;AAAA,cAAiB,OAAO,OAAO,KAAK;AAAA,eAAkB,OAAO,OAAO,QAAQ,OAAO,OAAO,OAAO;AAAA,YAC7K;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,MAAMA,GAAE,OAAO,EAAE,SAAS,0EAA0E;AAAA,MACpG,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,MACvF,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2DAA2D;AAAA,IAC5G;AAAA,IACA,OAAO,EAAE,YAAY,MAAM,SAAS,eAAe,MAAM;AACvD,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,WAAW,YAAY;AAAA,UACjD;AAAA,UACA,SAAS,WAAW;AAAA,UACpB,eAAe;AAAA,QACjB,CAAC;AAED,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA,cAA6C,OAAO,SAAS;AAAA,UAAa,OAAO,OAAO,KAAK;AAAA,WAAc,OAAO,OAAO,QAAQ,OAAO,OAAO,OAAO;AAAA,YAC9J;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,MAAMA,GAAE,OAAO,EAAE,SAAS,4CAA4C;AAAA,MACtE,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2DAA2D;AAAA,IAC5G;AAAA,IACA,OAAO,EAAE,YAAY,MAAM,eAAe,MAAM;AAC9C,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,WAAW,YAAY;AAAA,UACjD;AAAA,UACA,eAAe;AAAA,QACjB,CAAC;AAED,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA,UAAyC,OAAO,OAAO,KAAK;AAAA,WAAc,OAAO,OAAO,QAAQ,OAAO,OAAO,OAAO;AAAA,YAC7H;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,MAAMA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MACxD,SAASA,GAAE,OAAO,EAAE,SAAS,mBAAmB;AAAA,MAChD,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2DAA2D;AAAA,IAC5G;AAAA,IACA,OAAO,EAAE,YAAY,MAAM,SAAS,eAAe,MAAM;AACvD,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,aAAa,YAAY;AAAA,UACnD;AAAA,UACA;AAAA,UACA,eAAe;AAAA,QACjB,CAAC;AAED,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA;AAAA,UAA6C,OAAO,OAAO,KAAK;AAAA,WAAc,OAAO,OAAO,QAAQ,OAAO,OAAO,OAAO;AAAA,YACjI;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,YAAYA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC9D,OAAOA,GAAE,OAAO,EAAE,SAAS,wDAAwD;AAAA,MACnF,YAAYA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,kDAAkD;AAAA,MACtG,OAAOA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD;AAAA,IAC3F;AAAA,IACA,OAAO,EAAE,YAAY,OAAO,YAAY,MAAM,MAAM;AAClD,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,YAAY,YAAY;AAAA,UAClD;AAAA,UACA,WAAW;AAAA,UACX;AAAA,QACF,CAAC;AAED,YAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,yBAAyB,KAAK;AAAA,cACtC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,cAAM,SAAmB;AAAA,UACvB,yBAAyB,KAAK;AAAA,UAC9B;AAAA,UACA,SAAS,OAAO,QAAQ,MAAM;AAAA,UAC9B;AAAA,QACF;AAEA,mBAAW,SAAS,OAAO,SAAS;AAClC,iBAAO,KAAK,MAAM,MAAM,KAAK,IAAI,EAAE;AACnC,iBAAO,KAAK,UAAU,MAAM,KAAK,SAAS,eAAe,MAAM,UAAU,EAAE;AAC3E,iBAAO,KAAK,EAAE;AAEd,qBAAW,WAAW,MAAM,UAAU;AACpC,mBAAO,KAAK,KAAK,QAAQ,IAAI,EAAE;AAC/B,mBAAO,KAAK,EAAE;AAAA,UAChB;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,KAAK,IAAI,EAAE,CAAC;AAAA,QACrD;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,KAAK,EAAE,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACnUA,IAAM,UAAU,QAAQ,IAAI,UAAU,UAAU,QAAQ,IAAI,UAAU;AAE/D,IAAM,SAAS;AAAA,EACpB,MAAM,CAAC,YAAoB,SAAoB;AAC7C,YAAQ,MAAM,UAAU,OAAO,IAAI,GAAG,IAAI;AAAA,EAC5C;AAAA,EAEA,MAAM,CAAC,YAAoB,SAAoB;AAC7C,YAAQ,MAAM,UAAU,OAAO,IAAI,GAAG,IAAI;AAAA,EAC5C;AAAA,EAEA,OAAO,CAAC,SAAiB,UAAoB;AAC3C,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,MAAM,WAAW,OAAO,KAAK,MAAM,OAAO;AAClD,UAAI,WAAW,MAAM,OAAO;AAC1B,gBAAQ,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,IACF,WAAW,UAAU,QAAW;AAC9B,cAAQ,MAAM,WAAW,OAAO,KAAK,KAAK;AAAA,IAC5C,OAAO;AACL,cAAQ,MAAM,WAAW,OAAO,EAAE;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,OAAO,CAAC,YAAoB,SAAoB;AAC9C,QAAI,SAAS;AACX,cAAQ,MAAM,WAAW,OAAO,IAAI,GAAG,IAAI;AAAA,IAC7C;AAAA,EACF;AACF;;;APZO,SAAS,kBAAkB,QAAiC;AAEjE,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,SAAS,OAAO,WAAW;AAAA,IAC3B,UAAU,YAAY,OAAO;AAAA,IAC7B;AAAA,EACF,CAAC;AAED,QAAM,SAAS,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAGD,oBAAkB,QAAQ,MAAM;AAChC,uBAAqB,QAAQ,MAAM;AACnC,0BAAwB,QAAQ,QAAQ,MAAM;AAC9C,oBAAkB,QAAQ,MAAM;AAEhC,SAAO,KAAK,8BAA8B;AAC1C,SAAO,KAAK,iBAAiB,OAAO,WAAW,sBAAsB,EAAE;AAEvE,SAAO;AACT;;;AQxCA,SAAS,YAAY,UAAU;AAC/B,SAAS,eAAe;AACxB,SAAS,YAAY;AAErB,IAAM,YAAY;AAClB,IAAM,mBAAmB;AAQzB,SAAS,qBAA6B;AACpC,SAAO,KAAK,QAAQ,GAAG,WAAW,gBAAgB;AACpD;AAEA,SAAS,cAAsB;AAC7B,SAAO,KAAK,QAAQ,GAAG,SAAS;AAClC;AAKA,eAAsB,UAAU,UAAkB,SAAiC;AACjF,QAAM,WAAW,YAAY;AAG7B,QAAM,GAAG,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAE5C,QAAM,cAA2B;AAAA,IAC/B;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,GAAI,WAAW,EAAE,QAAQ;AAAA,EAC3B;AAEA,QAAM,kBAAkB,mBAAmB;AAC3C,QAAM,GAAG,UAAU,iBAAiB,KAAK,UAAU,aAAa,MAAM,CAAC,GAAG,OAAO;AAGjF,QAAM,GAAG,MAAM,iBAAiB,GAAK;AACvC;AAKA,eAAsB,YAAyC;AAC7D,MAAI;AACF,UAAM,kBAAkB,mBAAmB;AAC3C,UAAM,UAAU,MAAM,GAAG,SAAS,iBAAiB,OAAO;AAC1D,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,cAA6B;AACjD,MAAI;AACF,UAAM,kBAAkB,mBAAmB;AAC3C,UAAM,GAAG,OAAO,eAAe;AAAA,EACjC,QAAQ;AAAA,EAER;AACF;AAaO,SAAS,yBAAiC;AAC/C,SAAO,mBAAmB;AAC5B;;;AC9EA,SAAS,oBAA+D;AACxE,SAAS,WAAW;AACpB,SAAS,mBAAmB;AAI5B,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAK1B,eAAe,YAAY,KAA4B;AACrD,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,eAAoB;AAClD,QAAM,EAAE,UAAU,IAAI,MAAM,OAAO,MAAW;AAC9C,QAAM,YAAY,UAAU,IAAI;AAEhC,QAAM,WAAW,QAAQ;AAEzB,MAAI;AACF,QAAI,aAAa,UAAU;AACzB,YAAM,UAAU,SAAS,GAAG,GAAG;AAAA,IACjC,WAAW,aAAa,SAAS;AAC/B,YAAM,UAAU,aAAa,GAAG,GAAG;AAAA,IACrC,OAAO;AAGL,YAAM,WAAW,CAAC,YAAY,oBAAoB,SAAS;AAC3D,iBAAW,OAAO,UAAU;AAC1B,YAAI;AACF,gBAAM,UAAU,GAAG,GAAG,KAAK,GAAG,GAAG;AACjC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,cAAQ,IAAI;AAAA;AAAA,EAA4C,GAAG;AAAA,CAAI;AAAA,IACjE;AAAA,EACF,QAAQ;AACN,YAAQ,IAAI;AAAA;AAAA,EAA4C,GAAG;AAAA,CAAI;AAAA,EACjE;AACF;AAKA,eAAe,oBAAqC;AAClD,WAAS,OAAO,qBAAqB,QAAQ,mBAAmB,QAAQ;AACtE,UAAM,YAAY,MAAM,gBAAgB,IAAI;AAC5C,QAAI,WAAW;AACb,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,IAAI,MAAM,8BAA8B,mBAAmB,QAAQ,iBAAiB,EAAE;AAC9F;AAEA,SAAS,gBAAgB,MAAgC;AACvD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,SAAS,aAAa;AAC5B,WAAO,KAAK,SAAS,MAAM,QAAQ,KAAK,CAAC;AACzC,WAAO,KAAK,aAAa,MAAM;AAC7B,aAAO,MAAM,MAAM,QAAQ,IAAI,CAAC;AAAA,IAClC,CAAC;AACD,WAAO,OAAO,MAAM,WAAW;AAAA,EACjC,CAAC;AACH;AAKA,SAAS,gBAAwB;AAC/B,SAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACvC;AAMA,SAAS,iBAAyB;AAChC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoGT;AAQA,SAAS,WAAW,MAAsB;AACxC,QAAM,MAA8B;AAAA,IAClC,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,SAAO,KAAK,QAAQ,YAAY,CAAC,SAAS,IAAI,IAAI,CAAC;AACrD;AAMA,SAAS,aAAa,SAAyB;AAC7C,QAAM,cAAc,WAAW,OAAO;AACtC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAwGgB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOpC;AAOA,eAAsB,QAAQ,UAAiD,CAAC,GAAkB;AAChG,QAAM,UAAU,QAAQ,WAAW,QAAQ,IAAI,iBAAiB;AAChE,QAAM,YAAY,QAAQ,SAAS;AAEnC,UAAQ,IAAI,wCAAiC;AAG7C,QAAM,sBAAsB,MAAM,UAAU;AAC5C,MAAI,uBAAuB,CAAC,WAAW;AACrC,YAAQ,IAAI,gCAAgC;AAC5C,YAAQ,IAAI,oBAAoB,uBAAuB,CAAC,EAAE;AAC1D,YAAQ,IAAI,8EAA8E;AAC1F;AAAA,EACF;AAGA,MAAI,uBAAuB,WAAW;AACpC,UAAM,YAAY;AAClB,YAAQ,IAAI,0DAA0D;AAAA,EACxE;AAEA,QAAM,QAAQ,cAAc;AAC5B,QAAM,OAAO,MAAM,kBAAkB;AAErC,UAAQ,IAAI,sDAAsD;AAGlE,QAAM,eAAe,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC5D,UAAM,SAAS,aAAa,CAAC,KAAsB,QAAwB;AACzE,YAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,oBAAoB,IAAI,EAAE;AAE9D,UAAI,IAAI,aAAa,aAAa;AAChC,cAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAC1C,cAAM,gBAAgB,IAAI,aAAa,IAAI,OAAO;AAClD,cAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAE1C,YAAI,OAAO;AACT,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI,IAAI,aAAa,KAAK,CAAC;AAC3B,iBAAO,MAAM;AACb,iBAAO,IAAI,MAAM,KAAK,CAAC;AACvB;AAAA,QACF;AAEA,YAAI,kBAAkB,OAAO;AAC3B,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI,IAAI,aAAa,4CAA4C,CAAC;AAClE,iBAAO,MAAM;AACb,iBAAO,IAAI,MAAM,gBAAgB,CAAC;AAClC;AAAA,QACF;AAEA,YAAI,CAAC,OAAO;AACV,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI,IAAI,aAAa,sCAAsC,CAAC;AAC5D,iBAAO,MAAM;AACb,iBAAO,IAAI,MAAM,mBAAmB,CAAC;AACrC;AAAA,QACF;AAEA,YAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,YAAI,IAAI,eAAe,CAAC;AACxB,eAAO,MAAM;AACb,gBAAQ,KAAK;AAAA,MACf,OAAO;AACL,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI,WAAW;AAAA,MACrB;AAAA,IACF,CAAC;AAED,WAAO,OAAO,MAAM,aAAa,MAAM;AACrC,cAAQ,IAAI,iCAAiC,IAAI,EAAE;AAAA,IACrD,CAAC;AAGD,eAAW,MAAM;AACf,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,6CAA6C,CAAC;AAAA,IACjE,GAAG,GAAK;AAAA,EACV,CAAC;AAID,QAAM,cAAc,YAAY,kBAAkB;AAClD,QAAM,UAAU,GAAG,OAAO,0BAA0B,KAAK,kBAAkB,IAAI,GAAG,WAAW;AAE7F,UAAQ,IAAI,yCAAyC;AACrD,QAAM,YAAY,OAAO;AAEzB,UAAQ,IAAI,iCAAiC;AAC7C,UAAQ,IAAI,4BAA4B;AAExC,MAAI;AACF,UAAM,QAAQ,MAAM;AACpB,UAAM,UAAU,OAAO,YAAY,kBAAkB,UAAU,MAAS;AAExE,YAAQ,IAAI,qCAAgC;AAC5C,YAAQ,IAAI,mBAAmB,uBAAuB,CAAC,EAAE;AACzD,YAAQ,IAAI,gEAAgE;AAAA,EAC9E,SAAS,OAAO;AACd,YAAQ,MAAM,mCAA8B,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC1F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAKA,eAAsB,YAA2B;AAC/C,UAAQ,IAAI,gCAAyB;AAErC,QAAM,cAAc,MAAM,UAAU;AACpC,MAAI,CAAC,aAAa;AAChB,YAAQ,IAAI,8BAA8B;AAC1C;AAAA,EACF;AAEA,QAAM,YAAY;AAClB,UAAQ,IAAI,iCAA4B;AACxC,UAAQ,IAAI,6BAA6B,uBAAuB,CAAC;AAAA,CAAI;AACvE;AAKA,eAAsB,YAA2B;AAC/C,UAAQ,IAAI,gCAAyB;AAErC,QAAM,cAAc,MAAM,UAAU;AACpC,MAAI,CAAC,aAAa;AAChB,YAAQ,IAAI,2BAA2B;AACvC,YAAQ,IAAI,2CAA2C;AACvD;AAAA,EACF;AAEA,UAAQ,IAAI,uBAAuB;AACnC,UAAQ,IAAI,UAAU,YAAY,SAAS,UAAU,GAAG,EAAE,CAAC,KAAK;AAChE,UAAQ,IAAI,qBAAqB,YAAY,SAAS,EAAE;AACxD,MAAI,YAAY,SAAS;AACvB,YAAQ,IAAI,YAAY,YAAY,OAAO,EAAE;AAAA,EAC/C;AACA,UAAQ,IAAI,qBAAqB,uBAAuB,CAAC;AAAA,CAAI;AAC/D;;;AV1cA,IAAM,UAAU;AAEhB,SAAS,WAAiB;AACxB,UAAQ,IAAI;AAAA,aACD,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAyCnB;AACD;AAEA,SAAS,cAAoB;AAC3B,UAAQ,IAAI,cAAc,OAAO,EAAE;AACrC;AAEA,eAAe,cAA6B;AAE1C,MAAI,WAAW,QAAQ,IAAI;AAC3B,MAAI,UAAU,QAAQ,IAAI;AAE1B,MAAI,CAAC,UAAU;AACb,UAAM,cAAc,MAAM,UAAU;AACpC,QAAI,aAAa;AACf,iBAAW,YAAY;AACvB,gBAAU,WAAW,YAAY;AAAA,IACnC;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,WAAO,MAAM,+CAA+C;AAC5D,WAAO,MAAM,EAAE;AACf,WAAO,MAAM,uBAAuB;AACpC,WAAO,MAAM,kBAAkB;AAC/B,WAAO,MAAM,EAAE;AACf,WAAO,MAAM,8BAA8B;AAC3C,WAAO,MAAM,wCAAwC;AACrD,WAAO,MAAM,EAAE;AACf,WAAO,MAAM,mEAAmE;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACF,UAAM,SAAS,kBAAkB;AAAA,MAC/B;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,QAAQ,SAAS;AAE9B,WAAO,KAAK,mCAAmC;AAG/C,YAAQ,GAAG,UAAU,YAAY;AAC/B,aAAO,KAAK,kBAAkB;AAC9B,YAAM,OAAO,MAAM;AACnB,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAED,YAAQ,GAAG,WAAW,YAAY;AAChC,aAAO,KAAK,kBAAkB;AAC9B,YAAM,OAAO,MAAM;AACnB,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,0BAA0B,KAAK;AAC5C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,UAAU,KAAK,CAAC;AAEtB,UAAQ,SAAS;AAAA,IACf,KAAK,QAAQ;AACX,YAAM,YAAY,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,IAAI;AAChE,YAAM,QAAQ,EAAE,OAAO,UAAU,CAAC;AAClC;AAAA,IACF;AAAA,IAEA,KAAK;AACH,YAAM,UAAU;AAChB;AAAA,IAEF,KAAK;AACH,YAAM,UAAU;AAChB;AAAA,IAEF,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,eAAS;AACT;AAAA,IAEF,KAAK;AAAA,IACL,KAAK;AACH,kBAAY;AACZ;AAAA,IAEF,KAAK;AAEH,YAAM,YAAY;AAClB;AAAA,IAEF;AACE,cAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,cAAQ,MAAM,mCAAmC;AACjD,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,KAAK;","names":["z","z","result","z","defaultLogger","logger","z","findFileInSnapshot","result","searchByName","z","z"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/server.ts","../src/core/utils/errors.ts","../src/core/api/client.ts","../src/core/tools/books.ts","../src/core/utils/file-tree.ts","../src/core/tools/content.ts","../src/core/tools/beta-reader.ts","../src/core/tools/files.ts","../src/core/utils/logger.ts","../src/cli/token-storage.ts","../src/cli/auth.ts"],"sourcesContent":["/**\n * Slima MCP Server - CLI Entry Point\n *\n * Usage:\n * slima-mcp - Start MCP server (requires SLIMA_API_TOKEN or prior auth)\n * slima-mcp auth - Authenticate with Slima (opens browser)\n * slima-mcp logout - Remove saved credentials\n * slima-mcp status - Show authentication status\n * slima-mcp --help - Show help\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createSlimaServer } from './server.js';\nimport { logger } from '../core/utils/logger.js';\nimport { loadToken, getCredentialsFilePath } from './token-storage.js';\nimport { runAuth, runLogout, runStatus } from './auth.js';\n\ndeclare const __VERSION__: string;\nconst VERSION = __VERSION__;\n\nfunction showHelp(): void {\n console.log(`\nSlima MCP v${VERSION}\nMCP Server for Slima - AI Writing IDE for Novel Authors\n\nUSAGE:\n slima-mcp [command]\n\nCOMMANDS:\n (none) Start the MCP server\n auth Authenticate with Slima (opens browser)\n auth --force Re-authenticate (switch account)\n logout Remove saved credentials\n status Show authentication status\n --help Show this help message\n --version Show version\n\nAUTHENTICATION:\n 1. Run: slima-mcp auth\n 2. Complete authentication in browser\n 3. Token is automatically saved\n\n Or set SLIMA_API_TOKEN environment variable.\n\nEXAMPLES:\n slima-mcp auth # Authenticate with browser\n slima-mcp auth --force # Re-authenticate (switch account)\n slima-mcp status # Check auth status\n SLIMA_API_TOKEN=xxx slima-mcp # Use env var\n\nCONFIGURATION (for AI tools):\n Claude Desktop, Cursor, etc.:\n {\n \"mcpServers\": {\n \"slima\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"slima-mcp\"]\n }\n }\n }\n\nMORE INFO:\n https://github.com/slima-ai/slima-mcp\n`);\n}\n\nfunction showVersion(): void {\n console.log(`slima-mcp v${VERSION}`);\n}\n\nasync function startServer(): Promise<void> {\n // Use env var first, then stored token\n let apiToken = process.env.SLIMA_API_TOKEN;\n let baseUrl = process.env.SLIMA_API_URL;\n\n if (!apiToken) {\n const credentials = await loadToken();\n if (credentials) {\n apiToken = credentials.apiToken;\n baseUrl = baseUrl || credentials.baseUrl;\n }\n }\n\n if (!apiToken) {\n logger.error('Not authenticated and SLIMA_API_TOKEN not set');\n logger.error('');\n logger.error('To authenticate, run:');\n logger.error(' slima-mcp auth');\n logger.error('');\n logger.error('Or set environment variable:');\n logger.error(' SLIMA_API_TOKEN=your_token slima-mcp');\n logger.error('');\n logger.error('Get your API token from: https://app.slima.ai/settings/api-tokens');\n process.exit(1);\n }\n\n try {\n const server = createSlimaServer({\n apiToken,\n baseUrl,\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n logger.info('Slima MCP Server running on stdio');\n\n // Graceful shutdown\n process.on('SIGINT', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n logger.info('Shutting down...');\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n logger.error('Failed to start server', error);\n process.exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const command = args[0];\n\n switch (command) {\n case 'auth': {\n const forceFlag = args.includes('--force') || args.includes('-f');\n await runAuth({ force: forceFlag });\n break;\n }\n\n case 'logout':\n await runLogout();\n break;\n\n case 'status':\n await runStatus();\n break;\n\n case '--help':\n case '-h':\n case 'help':\n showHelp();\n break;\n\n case '--version':\n case '-v':\n showVersion();\n break;\n\n case undefined:\n // Default: start MCP server\n await startServer();\n break;\n\n default:\n console.error(`Unknown command: ${command}`);\n console.error('Run `slima-mcp --help` for usage.');\n process.exit(1);\n }\n}\n\nmain();\n","/**\n * Slima MCP Server (CLI/stdio)\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { SlimaApiClient } from '../core/api/client.js';\nimport {\n registerBookTools,\n registerContentTools,\n registerBetaReaderTools,\n registerFileTools,\n} from '../core/tools/index.js';\nimport { logger } from '../core/utils/logger.js';\n\ndeclare const __VERSION__: string;\n\nexport interface ServerConfig {\n apiToken: string;\n baseUrl?: string;\n}\n\n/**\n * Create Slima MCP Server\n */\nexport function createSlimaServer(config: ServerConfig): McpServer {\n // Create API Client with static token getter (for CLI)\n const client = new SlimaApiClient({\n baseUrl: config.baseUrl || 'https://api.slima.ai',\n getToken: async () => config.apiToken,\n logger,\n });\n\n const server = new McpServer({\n name: 'slima',\n version: __VERSION__,\n });\n\n // Register tools\n registerBookTools(server, client);\n registerContentTools(server, client);\n registerBetaReaderTools(server, client, logger);\n registerFileTools(server, client);\n\n logger.info('Slima MCP Server initialized');\n logger.info(`API endpoint: ${config.baseUrl || 'https://api.slima.ai'}`);\n\n return server;\n}\n","/**\n * 錯誤處理工具\n */\n\nexport class SlimaApiError extends Error {\n constructor(\n public status: number,\n public code?: string,\n message?: string\n ) {\n super(message || `API Error: ${status}`);\n this.name = 'SlimaApiError';\n }\n}\n\nexport class AuthenticationError extends SlimaApiError {\n constructor(message = 'Invalid or expired API token') {\n super(401, 'UNAUTHORIZED', message);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class NotFoundError extends SlimaApiError {\n constructor(resource = 'Resource') {\n super(404, 'NOT_FOUND', `${resource} not found`);\n this.name = 'NotFoundError';\n }\n}\n\nexport class InsufficientCreditsError extends SlimaApiError {\n constructor() {\n super(402, 'INSUFFICIENT_CREDITS', 'Insufficient credits');\n this.name = 'InsufficientCreditsError';\n }\n}\n\n/**\n * 格式化錯誤訊息供 MCP 回傳\n */\nexport function formatErrorForMcp(error: unknown): string {\n if (error instanceof SlimaApiError) {\n switch (error.code) {\n case 'NOT_FOUND':\n return 'Resource not found. Please check the token.';\n case 'UNAUTHORIZED':\n return 'Invalid API token. Please check your configuration.';\n case 'INSUFFICIENT_CREDITS':\n return 'Insufficient credits. Please top up your account.';\n case 'TOKEN_EXPIRED':\n return 'API token has expired. Please generate a new one.';\n case 'TOKEN_REVOKED':\n return 'API token has been revoked. Please generate a new one.';\n default:\n return error.message;\n }\n }\n\n if (error instanceof Error) {\n return error.message;\n }\n\n return 'An unexpected error occurred.';\n}\n","/**\n * Slima API Client\n *\n * Supports dependency injection for token retrieval,\n * allowing different strategies for CLI (file-based) vs Worker (KV-based).\n */\n\nimport {\n type Book,\n type Commit,\n type Branch,\n type Blob,\n type Persona,\n type ReaderTest,\n type ReaderTestProgress,\n type CreateReaderTestParams,\n type ApiResponse,\n type ApiError,\n type CommitsListResponse,\n type BlobsDownloadResponse,\n type McpFileReadResponse,\n type McpFileCreateResponse,\n type McpFileUpdateResponse,\n type McpFileDeleteResponse,\n type McpFileAppendResponse,\n type McpSearchResponse,\n} from './types.js';\nimport {\n SlimaApiError,\n AuthenticationError,\n NotFoundError,\n InsufficientCreditsError,\n} from '../utils/errors.js';\n\n// Export for logger injection (optional)\nexport interface Logger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, error?: unknown) => void;\n}\n\n// Default no-op logger\nconst defaultLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * API Client Configuration (new style with token getter)\n */\nexport interface ApiClientConfig {\n baseUrl: string;\n getToken: () => Promise<string>; // Async token getter (injected)\n logger?: Logger;\n}\n\n/**\n * Legacy config format (backwards compatibility)\n * @deprecated Use ApiClientConfig with getToken instead\n */\nexport interface SlimaConfig {\n baseUrl: string;\n token: string;\n}\n\n// Type guard to check if config is legacy format\nfunction isLegacyConfig(config: ApiClientConfig | SlimaConfig): config is SlimaConfig {\n return 'token' in config && typeof (config as SlimaConfig).token === 'string';\n}\n\nexport class SlimaApiClient {\n private readonly baseUrl: string;\n private readonly getToken: () => Promise<string>;\n private readonly logger: Logger;\n\n constructor(config: ApiClientConfig | SlimaConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\n // Support both legacy and new config formats\n if (isLegacyConfig(config)) {\n const staticToken = config.token;\n this.getToken = async () => staticToken;\n this.logger = defaultLogger;\n } else {\n this.getToken = config.getToken;\n this.logger = config.logger || defaultLogger;\n }\n }\n\n /**\n * Send API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const token = await this.getToken();\n\n this.logger.debug(`${method} ${path}`);\n\n const response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'slima-mcp-server/0.1.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n await this.handleErrorResponse(response);\n }\n\n const json = (await response.json()) as ApiResponse<T>;\n return json.data;\n }\n\n /**\n * Handle error response\n */\n private async handleErrorResponse(response: Response): Promise<never> {\n let errorData: ApiError | undefined;\n\n try {\n errorData = (await response.json()) as ApiError;\n } catch {\n // Cannot parse JSON\n }\n\n const code = errorData?.error?.code;\n const message = errorData?.error?.message;\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message);\n case 404:\n throw new NotFoundError(message);\n case 402:\n throw new InsufficientCreditsError();\n default:\n throw new SlimaApiError(response.status, code, message);\n }\n }\n\n // === Book Operations ===\n\n /**\n * List all books\n */\n async listBooks(): Promise<Book[]> {\n return this.request<Book[]>('GET', '/api/v1/books');\n }\n\n /**\n * Get book details\n */\n async getBook(token: string): Promise<Book> {\n return this.request<Book>('GET', `/api/v1/books/${token}`);\n }\n\n /**\n * Create a new book\n */\n async createBook(params: {\n title: string;\n authorName?: string;\n description?: string;\n }): Promise<Book> {\n return this.request<Book>('POST', '/api/v1/books', {\n book: {\n title: params.title,\n author_name: params.authorName,\n description: params.description,\n },\n });\n }\n\n // === Version Control ===\n\n /**\n * List commits\n */\n async listCommits(bookToken: string, limit = 10): Promise<Commit[]> {\n const response = await this.request<CommitsListResponse>(\n 'GET',\n `/api/v1/books/${bookToken}/commits?limit=${limit}`\n );\n return response.commits;\n }\n\n /**\n * Get commit details\n */\n async getCommit(bookToken: string, commitToken: string): Promise<Commit> {\n return this.request<Commit>(\n 'GET',\n `/api/v1/books/${bookToken}/commits/${commitToken}`\n );\n }\n\n /**\n * List branches\n */\n async listBranches(bookToken: string): Promise<Branch[]> {\n return this.request<Branch[]>('GET', `/api/v1/books/${bookToken}/branches`);\n }\n\n /**\n * Download blobs\n */\n async downloadBlobs(bookToken: string, hashes: string[]): Promise<Blob[]> {\n const response = await this.request<BlobsDownloadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/blobs/download`,\n { hashes }\n );\n return response.blobs;\n }\n\n // === Persona ===\n\n /**\n * List personas\n */\n async listPersonas(genre?: string): Promise<Persona[]> {\n const params = genre ? `?genre=${encodeURIComponent(genre)}` : '';\n return this.request<Persona[]>('GET', `/api/v1/personas${params}`);\n }\n\n /**\n * Get persona details\n */\n async getPersona(token: string): Promise<Persona> {\n return this.request<Persona>('GET', `/api/v1/personas/${token}`);\n }\n\n // === Reader Test ===\n\n /**\n * Create reader test\n */\n async createReaderTest(\n bookToken: string,\n params: CreateReaderTestParams\n ): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'POST',\n `/api/v1/books/${bookToken}/reader_tests`,\n {\n reader_test: {\n report_type: params.reportType || 'chapter',\n persona_tokens: params.personaTokens,\n commit_token: params.commitToken,\n model: params.model,\n scope_config: params.scopeConfig,\n },\n content: params.content,\n }\n );\n }\n\n /**\n * Get reader test progress\n */\n async getReaderTestProgress(\n bookToken: string,\n testToken: string\n ): Promise<ReaderTestProgress> {\n return this.request<ReaderTestProgress>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}/progress`\n );\n }\n\n /**\n * Get reader test result\n */\n async getReaderTest(bookToken: string, testToken: string): Promise<ReaderTest> {\n return this.request<ReaderTest>(\n 'GET',\n `/api/v1/books/${bookToken}/reader_tests/${testToken}`\n );\n }\n\n // === MCP File Operations ===\n\n /**\n * Read file content\n */\n async readFile(bookToken: string, path: string): Promise<McpFileReadResponse> {\n return this.request<McpFileReadResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/read`,\n { path }\n );\n }\n\n /**\n * Create file\n */\n async createFile(\n bookToken: string,\n params: { path: string; content?: string; parentPath?: string; commitMessage?: string }\n ): Promise<McpFileCreateResponse> {\n return this.request<McpFileCreateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/create`,\n {\n path: params.path,\n content: params.content || '',\n parent_path: params.parentPath,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Update (overwrite) file content\n */\n async updateFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileUpdateResponse> {\n return this.request<McpFileUpdateResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/update`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Delete file\n */\n async deleteFile(\n bookToken: string,\n params: { path: string; commitMessage?: string }\n ): Promise<McpFileDeleteResponse> {\n return this.request<McpFileDeleteResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/delete`,\n {\n path: params.path,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Append content to file\n */\n async appendToFile(\n bookToken: string,\n params: { path: string; content: string; commitMessage?: string }\n ): Promise<McpFileAppendResponse> {\n return this.request<McpFileAppendResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/append`,\n {\n path: params.path,\n content: params.content,\n commit_message: params.commitMessage,\n }\n );\n }\n\n /**\n * Search file content\n */\n async searchFiles(\n bookToken: string,\n params: { query: string; fileTypes?: string[]; limit?: number }\n ): Promise<McpSearchResponse> {\n return this.request<McpSearchResponse>(\n 'POST',\n `/api/v1/books/${bookToken}/mcp/files/search`,\n {\n query: params.query,\n file_types: params.fileTypes,\n limit: params.limit,\n }\n );\n }\n}\n","/**\n * Book Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport { buildFileTree, formatFileTree } from '../utils/file-tree.js';\nimport type { FlatFileSnapshot } from '../utils/file-tree.js';\n\n/**\n * Register book-related tools\n */\nexport function registerBookTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === create_book ===\n server.tool(\n 'create_book',\n 'Create a new book in your Slima library',\n {\n title: z.string().describe('Book title (required)'),\n author_name: z.string().optional().describe('Author name (optional)'),\n description: z.string().optional().describe('Book description (optional)'),\n },\n async ({ title, author_name, description }) => {\n try {\n const book = await client.createBook({\n title,\n authorName: author_name,\n description,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ Book created successfully!\\n\\n- **Title**: ${book.title}\\n- **Token**: ${book.token}\\n- **Author**: ${book.authorName || 'N/A'}\\n\\nYou can now add files using create_file with book_token: ${book.token}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === list_books ===\n server.tool(\n 'list_books',\n 'List all books in your Slima library',\n {},\n async () => {\n try {\n const books = await client.listBooks();\n\n if (books.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No books found in your library.',\n },\n ],\n };\n }\n\n const formatted = books\n .map((book) => {\n const words = book.totalWordCount?.toLocaleString() || '0';\n return `- **${book.title}** (${book.token})\\n Author: ${book.authorName || 'N/A'} | Words: ${words}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Found ${books.length} book(s):\\n\\n${formatted}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book ===\n server.tool(\n 'get_book',\n 'Get details of a specific book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n },\n async ({ book_token }) => {\n try {\n const book = await client.getBook(book_token);\n\n const text = `# ${book.title}\n\n- **Token**: ${book.token}\n- **Author**: ${book.authorName || 'N/A'}\n- **Description**: ${book.description || 'N/A'}\n- **Language**: ${book.language || 'N/A'}\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Created**: ${book.createdAt}\n- **Updated**: ${book.updatedAt}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_book_structure ===\n server.tool(\n 'get_book_structure',\n 'Get the file/folder structure of a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n // API returns flat structure with parentToken, convert to nested for display\n const flatSnapshot = commits[0].filesSnapshot as unknown as FlatFileSnapshot[];\n const nestedTree = buildFileTree(flatSnapshot);\n const tree = formatFileTree(nestedTree);\n\n return {\n content: [\n {\n type: 'text',\n text: `File structure for book ${book_token}:\\n\\n${tree}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === get_writing_stats ===\n server.tool(\n 'get_writing_stats',\n 'Get writing statistics for a book',\n {\n book_token: z.string().describe('Book token'),\n },\n async ({ book_token }) => {\n try {\n const [book, commits] = await Promise.all([\n client.getBook(book_token),\n client.listCommits(book_token, 10),\n ]);\n\n // Calculate recent progress\n let wordChange = 0;\n if (commits.length > 1) {\n const latest = commits[0].totalWordCount || 0;\n const oldest = commits[commits.length - 1].totalWordCount || 0;\n wordChange = latest - oldest;\n }\n\n const sign = wordChange >= 0 ? '+' : '';\n const progressText =\n commits.length > 1\n ? `${sign}${wordChange.toLocaleString()} words (last ${commits.length} commits)`\n : 'N/A (not enough history)';\n\n const text = `# Writing Stats: ${book.title}\n\n- **Total Words**: ${book.totalWordCount?.toLocaleString() || '0'}\n- **Manuscript Words**: ${book.manuscriptWordCount?.toLocaleString() || '0'}\n- **Recent Progress**: ${progressText}\n- **Commits Analyzed**: ${commits.length}\n- **Language**: ${book.language || 'N/A'}`;\n\n return {\n content: [{ type: 'text', text }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n","/**\n * File Tree Utilities\n *\n * Converts flat parentToken-based file structure (from API)\n * to nested children-based structure (for display).\n */\n\n/**\n * File snapshot with nested children (for display)\n */\nexport interface FileSnapshot {\n token: string;\n name: string;\n kind: 'file' | 'folder' | string;\n position: number;\n blobHash?: string;\n wordCount?: number;\n isManuscript?: boolean;\n children?: FileSnapshot[];\n}\n\n/**\n * Flat file snapshot with parentToken (from API)\n * Supports both camelCase and snake_case (API may return either)\n */\nexport interface FlatFileSnapshot {\n token: string;\n name: string;\n kind: 'file' | 'folder' | string;\n position: number;\n // camelCase\n blobHash?: string;\n wordCount?: number;\n isManuscript?: boolean;\n parentToken?: string;\n // snake_case (Rails API)\n blob_hash?: string;\n word_count?: number;\n is_manuscript?: boolean;\n parent_token?: string;\n}\n\n/**\n * Get parent token from file (supports both camelCase and snake_case)\n */\nfunction getParentToken(file: FlatFileSnapshot): string | undefined {\n return file.parentToken || file.parent_token;\n}\n\n/**\n * Get word count from file (supports both camelCase and snake_case)\n */\nfunction getWordCount(file: FlatFileSnapshot): number | undefined {\n return file.wordCount ?? file.word_count;\n}\n\n/**\n * Get isManuscript from file (supports both camelCase and snake_case)\n */\nfunction getIsManuscript(file: FlatFileSnapshot): boolean | undefined {\n return file.isManuscript ?? file.is_manuscript;\n}\n\n/**\n * Get blobHash from file (supports both camelCase and snake_case)\n */\nfunction getBlobHash(file: FlatFileSnapshot): string | undefined {\n return file.blobHash || file.blob_hash;\n}\n\n/**\n * Convert flat parentToken structure to nested children structure\n *\n * @param flatFiles - Array of files with parentToken references\n * @returns Array of root-level files with nested children\n */\nexport function buildFileTree(flatFiles: FlatFileSnapshot[]): FileSnapshot[] {\n if (flatFiles.length === 0) {\n return [];\n }\n\n // Create a map for quick lookup\n const fileMap = new Map<string, FileSnapshot>();\n\n // First pass: create all FileSnapshot objects (without children)\n for (const file of flatFiles) {\n fileMap.set(file.token, {\n token: file.token,\n name: file.name,\n kind: file.kind,\n position: file.position,\n blobHash: getBlobHash(file),\n wordCount: getWordCount(file),\n isManuscript: getIsManuscript(file),\n children: [],\n });\n }\n\n // Second pass: build parent-child relationships\n const rootFiles: FileSnapshot[] = [];\n\n for (const file of flatFiles) {\n const node = fileMap.get(file.token)!;\n const parentToken = getParentToken(file);\n\n if (parentToken) {\n // Has parent - add to parent's children\n const parent = fileMap.get(parentToken);\n if (parent) {\n parent.children = parent.children || [];\n parent.children.push(node);\n } else {\n // Parent not found - treat as root\n rootFiles.push(node);\n }\n } else {\n // No parent - this is a root node\n rootFiles.push(node);\n }\n }\n\n // Sort children by position recursively\n sortChildrenByPosition(rootFiles);\n\n // Clean up empty children arrays\n cleanEmptyChildren(rootFiles);\n\n return rootFiles;\n}\n\n/**\n * Recursively sort children by position\n */\nfunction sortChildrenByPosition(files: FileSnapshot[]): void {\n files.sort((a, b) => a.position - b.position);\n\n for (const file of files) {\n if (file.children && file.children.length > 0) {\n sortChildrenByPosition(file.children);\n }\n }\n}\n\n/**\n * Remove empty children arrays for cleaner output\n */\nfunction cleanEmptyChildren(files: FileSnapshot[]): void {\n for (const file of files) {\n if (file.children && file.children.length === 0) {\n delete file.children;\n } else if (file.children) {\n cleanEmptyChildren(file.children);\n }\n }\n}\n\n/**\n * Format nested file tree as display string\n *\n * @param files - Nested file structure (with children)\n * @param indent - Current indentation level\n * @returns Formatted tree string\n */\nexport function formatFileTree(files: FileSnapshot[], indent = 0): string {\n return files\n .sort((a, b) => a.position - b.position)\n .map((f) => {\n const prefix = ' '.repeat(indent);\n const icon = f.kind === 'folder' ? '📁' : '📄';\n const words = f.kind !== 'folder' && f.wordCount ? ` (${f.wordCount} words)` : '';\n const manuscript = f.isManuscript ? ' [M]' : '';\n let line = `${prefix}${icon} ${f.name}${words}${manuscript}`;\n\n if (f.children && f.children.length > 0) {\n line += '\\n' + formatFileTree(f.children, indent + 1);\n }\n\n return line;\n })\n .join('\\n');\n}\n","/**\n * Content Access Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot } from '../api/types.js';\n\n/**\n * Register content access tools\n */\nexport function registerContentTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === get_chapter ===\n server.tool(\n 'get_chapter',\n 'Read the content of a chapter or file from a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('File path (e.g., /chapters/01.md or chapter-01)'),\n },\n async ({ book_token, file_path }) => {\n try {\n // 1. Get latest commit\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n };\n }\n\n // 2. Find file in snapshot\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n // List available files\n const availableFiles = listAllFiles(commits[0].filesSnapshot);\n const fileList = availableFiles.slice(0, 10).join('\\n- ');\n const moreText =\n availableFiles.length > 10\n ? `\\n... and ${availableFiles.length - 10} more files`\n : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}\\n\\nAvailable files:\\n- ${fileList}${moreText}\\n\\nTip: Use get_book_structure to see the full file tree.`,\n },\n ],\n isError: true,\n };\n }\n\n if (file.kind === 'folder') {\n return {\n content: [\n {\n type: 'text',\n text: `\"${file_path}\" is a folder, not a file. Please specify a file path.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n };\n }\n\n // 3. Download blob content\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content. The blob may be missing.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n const wordInfo = file.wordCount ? ` (${file.wordCount} words)` : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `# ${file.name}${wordInfo}\\n\\n${content}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Find file in snapshot\n * Supports multiple path formats:\n * - Full path: /chapters/01.md\n * - Relative path: chapters/01.md\n * - Filename: 01.md\n * - Token: chapter-01\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n // Normalize path\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n // Recursive search\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n // Full path match\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n // Folder match, continue searching\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n // Also try searching in subfolders directly\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n // Try full path first\n let result = search(files, pathParts);\n if (result) return result;\n\n // Then try searching by filename only\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\n/**\n * Search by filename only\n */\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n\n/**\n * List all file paths\n */\nfunction listAllFiles(files: FileSnapshot[], prefix = ''): string[] {\n const result: string[] = [];\n\n for (const item of files) {\n const path = prefix ? `${prefix}/${item.name}` : item.name;\n\n if (item.kind === 'file') {\n result.push(path);\n } else if (item.children) {\n result.push(...listAllFiles(item.children, path));\n }\n }\n\n return result;\n}\n","/**\n * Beta Reader Tools\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient, Logger } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\nimport type { FileSnapshot, ReaderTest, IndividualFeedback, AggregatedMetrics } from '../api/types.js';\n\n// Default no-op logger\nconst defaultLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\n/**\n * Register Beta Reader tools\n */\nexport function registerBetaReaderTools(\n server: McpServer,\n client: SlimaApiClient,\n logger: Logger = defaultLogger\n): void {\n // === list_personas ===\n server.tool(\n 'list_personas',\n 'List available virtual reader personas for beta reading feedback',\n {\n genre: z\n .string()\n .optional()\n .describe('Filter by genre (e.g., fantasy, romance, scifi)'),\n },\n async ({ genre }) => {\n try {\n const personas = await client.listPersonas(genre);\n\n if (personas.length === 0) {\n const genreText = genre ? ` for genre \"${genre}\"` : '';\n return {\n content: [\n {\n type: 'text',\n text: `No personas found${genreText}.`,\n },\n ],\n };\n }\n\n const formatted = personas\n .map((p) => {\n const name = p.displayLabels?.zh || p.displayLabels?.en || p.slug;\n const tags = p.genreTags?.join(', ') || 'General';\n const demo = p.demographics || {};\n const gender = demo.gender || 'N/A';\n const age = demo.ageRange || 'N/A';\n\n return `- **${name}** (\\`${p.token}\\`)\n Gender: ${gender} | Age: ${age}\n Genres: ${tags}`;\n })\n .join('\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Available Personas (${personas.length}):\\n\\n${formatted}\\n\\n**Tip**: Use the persona token with \\`analyze_chapter\\` to get feedback from a specific reader.`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === analyze_chapter ===\n server.tool(\n 'analyze_chapter',\n 'Get AI Beta Reader feedback on a chapter from a specific reader persona. This may take a moment to complete.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n file_path: z.string().describe('Chapter file path (e.g., /chapters/01.md)'),\n persona_token: z\n .string()\n .describe('Persona token (e.g., psn_xxx). Use list_personas to find available personas.'),\n },\n async ({ book_token, file_path, persona_token }) => {\n try {\n // 1. Get latest commit and chapter content\n const commits = await client.listCommits(book_token, 1);\n\n if (commits.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No commits found. The book may be empty.',\n },\n ],\n isError: true,\n };\n }\n\n // 2. Find file\n const file = findFileInSnapshot(commits[0].filesSnapshot, file_path);\n\n if (!file) {\n return {\n content: [\n {\n type: 'text',\n text: `File not found: ${file_path}. Use get_book_structure to see available files.`,\n },\n ],\n isError: true,\n };\n }\n\n if (!file.blobHash) {\n return {\n content: [\n {\n type: 'text',\n text: `File \"${file_path}\" has no content (empty file).`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. Download content\n const blobs = await client.downloadBlobs(book_token, [file.blobHash]);\n\n if (blobs.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'Could not retrieve file content.',\n },\n ],\n isError: true,\n };\n }\n\n const content = blobs[0].content;\n\n // 4. Create Reader Test\n logger.info(`Creating reader test for ${file_path} with persona ${persona_token}`);\n\n const test = await client.createReaderTest(book_token, {\n reportType: 'chapter',\n personaTokens: [persona_token],\n commitToken: commits[0].token,\n scopeConfig: { chapters: [file_path] },\n content: content,\n });\n\n // 5. Poll for completion (max 90 seconds)\n const result = await pollForCompletion(\n () => client.getReaderTestProgress(book_token, test.token),\n 90000,\n logger\n );\n\n if (result.status === 'failed') {\n return {\n content: [\n {\n type: 'text',\n text: 'Analysis failed. Please try again later.',\n },\n ],\n isError: true,\n };\n }\n\n if (result.status !== 'completed') {\n return {\n content: [\n {\n type: 'text',\n text: `Analysis is still in progress. You can check the status later.\\n\\nTest ID: ${test.token}`,\n },\n ],\n };\n }\n\n // 6. Get full result\n const fullResult = await client.getReaderTest(book_token, test.token);\n\n // 7. Format feedback\n const feedback = formatBetaReaderFeedback(fullResult, file.name);\n\n return {\n content: [{ type: 'text', text: feedback }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Poll for completion\n */\nasync function pollForCompletion(\n checkFn: () => Promise<{ status: string; progress?: number }>,\n timeoutMs: number,\n logger: Logger\n): Promise<{ status: string }> {\n const startTime = Date.now();\n const pollInterval = 3000; // 3 seconds\n\n while (Date.now() - startTime < timeoutMs) {\n const result = await checkFn();\n logger.debug(`Poll status: ${result.status}, progress: ${result.progress || 'N/A'}`);\n\n if (result.status === 'completed' || result.status === 'failed') {\n return result;\n }\n\n await sleep(pollInterval);\n }\n\n return { status: 'timeout' };\n}\n\n/**\n * Sleep\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Format Beta Reader feedback\n */\nfunction formatBetaReaderFeedback(test: ReaderTest, fileName: string): string {\n const feedbacks = test.individualFeedbacks || [];\n const metrics = test.aggregatedMetrics;\n\n if (feedbacks.length === 0 && !metrics) {\n return 'No feedback available.';\n }\n\n let output = `# Beta Reader Feedback: ${fileName}\\n\\n`;\n\n // Aggregated metrics summary\n if (metrics) {\n output += formatAggregatedMetrics(metrics);\n }\n\n // Individual persona feedback\n if (feedbacks.length > 0) {\n output += `\\n---\\n\\n## Individual Reader Feedback\\n\\n`;\n output += feedbacks.map(formatIndividualFeedback).join('\\n---\\n\\n');\n }\n\n return output;\n}\n\n/**\n * Format aggregated metrics\n */\nfunction formatAggregatedMetrics(metrics: AggregatedMetrics): string {\n let output = `## Summary Metrics\\n\\n`;\n\n // Overall\n if (metrics.overall) {\n const o = metrics.overall;\n output += `### Overall Scores\\n`;\n if (o.avgContinueReading) output += `- Continue Reading Intent: ${o.avgContinueReading.toFixed(1)}/10\\n`;\n if (o.avgRecommendation) output += `- Recommendation Score: ${o.avgRecommendation.toFixed(1)}/10\\n`;\n if (o.avgWantMore) output += `- Want More: ${o.avgWantMore.toFixed(1)}/10\\n`;\n if (o.dnfRisk) output += `- DNF Risk: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (metrics.kindleRating) {\n output += `### Predicted Kindle Rating\\n`;\n output += `- Average Score: ⭐ ${metrics.kindleRating.avgScore.toFixed(1)}/5\\n`;\n if (metrics.kindleRating.individualScores) {\n const scores = metrics.kindleRating.individualScores\n .map(s => `${s.personaName || 'Reader'}: ${s.score}/5 (${s.confidence})`)\n .join(', ');\n output += `- Individual Scores: ${scores}\\n`;\n }\n output += '\\n';\n }\n\n // Characters\n if (metrics.characters) {\n const c = metrics.characters;\n output += `### Character Evaluation\\n`;\n if (c.avgProtagonistLikability) output += `- Protagonist Likability: ${c.avgProtagonistLikability.toFixed(1)}/10\\n`;\n if (c.avgMotivationClarity) output += `- Motivation Clarity: ${c.avgMotivationClarity.toFixed(1)}/10\\n`;\n if (c.avgDialogueNaturalness) output += `- Dialogue Naturalness: ${c.avgDialogueNaturalness.toFixed(1)}/10\\n`;\n output += '\\n';\n }\n\n // Pacing\n if (metrics.pacing) {\n const p = metrics.pacing;\n output += `### Pacing Evaluation\\n`;\n if (p.avgOverall) output += `- Overall Pacing: ${p.avgOverall.toFixed(1)}/10\\n`;\n if (p.consensus) output += `- Consensus: ${p.consensus}\\n`;\n if (p.commonDnfTriggers && p.commonDnfTriggers.length > 0) {\n output += `- Common DNF Triggers:\\n`;\n p.commonDnfTriggers.slice(0, 3).forEach(t => {\n output += ` - ${t.trigger} (${t.count}x)\\n`;\n });\n }\n output += '\\n';\n }\n\n // Market\n if (metrics.market) {\n const m = metrics.market;\n output += `### Market Positioning\\n`;\n if (m.avgWordOfMouth) output += `- Word of Mouth Potential: ${m.avgWordOfMouth.toFixed(1)}/10\\n`;\n if (m.topComparableBooks && m.topComparableBooks.length > 0) {\n const books = m.topComparableBooks.slice(0, 3).map(b => b.title).join(', ');\n output += `- Comparable Books: ${books}\\n`;\n }\n output += '\\n';\n }\n\n return output;\n}\n\n/**\n * Format individual feedback\n */\nfunction formatIndividualFeedback(feedback: IndividualFeedback): string {\n const name = feedback.personaName || feedback.personaToken;\n let output = `### ${name}\\n\\n`;\n\n // Overall scores\n if (feedback.overall) {\n const o = feedback.overall;\n if (o.continueReading) output += `**Continue Reading Intent**: ${o.continueReading}/10\\n`;\n if (o.recommendation) output += `**Recommendation**: ${o.recommendation}/10\\n`;\n if (o.dnfRisk) output += `**DNF Risk**: ${o.dnfRisk}\\n`;\n output += '\\n';\n }\n\n // Kindle Rating\n if (feedback.kindleRating) {\n output += `**Kindle Rating**: ⭐ ${feedback.kindleRating.score}/5`;\n if (feedback.kindleRating.rationale) {\n output += ` - ${feedback.kindleRating.rationale}`;\n }\n output += '\\n\\n';\n }\n\n // Detailed Feedback\n if (feedback.detailedFeedback) {\n const d = feedback.detailedFeedback;\n\n if (d.whatWorked) {\n output += `**What Worked**:\\n${d.whatWorked}\\n\\n`;\n }\n\n if (d.whatDidntWork) {\n output += `**What Didn't Work**:\\n${d.whatDidntWork}\\n\\n`;\n }\n\n if (d.strongestElement) {\n output += `**Strongest Element**: ${d.strongestElement}\\n`;\n }\n\n if (d.weakestElement) {\n output += `**Weakest Element**: ${d.weakestElement}\\n`;\n }\n\n if (d.wouldYouBuy) {\n output += `**Would You Buy**: ${d.wouldYouBuy}\\n`;\n }\n\n if (d.specificSuggestions && d.specificSuggestions.length > 0) {\n output += `\\n**Specific Suggestions**:\\n`;\n d.specificSuggestions.forEach((s, i) => {\n output += `${i + 1}. ${s}\\n`;\n });\n }\n\n if (d.finalThoughts) {\n output += `\\n**Final Thoughts**:\\n${d.finalThoughts}\\n`;\n }\n }\n\n // Emotional Journey\n if (feedback.emotionalJourney) {\n const e = feedback.emotionalJourney;\n if (e.emotionalArc) {\n output += `\\n**Emotional Arc**: ${e.emotionalArc}\\n`;\n }\n if (e.peakMoments && e.peakMoments.length > 0) {\n const moments = e.peakMoments.map(m =>\n typeof m === 'string' ? m : (m as { moment?: string; description?: string }).moment || (m as { moment?: string; description?: string }).description || JSON.stringify(m)\n );\n output += `**Peak Moments**: ${moments.join(', ')}\\n`;\n }\n if (e.overallMood) {\n output += `**Overall Mood**: ${e.overallMood}\\n`;\n }\n }\n\n return output;\n}\n\n/**\n * Find file in snapshot\n */\nfunction findFileInSnapshot(\n files: FileSnapshot[],\n path: string\n): FileSnapshot | null {\n const normalizedPath = path.replace(/^\\//, '').toLowerCase();\n const pathParts = normalizedPath.split('/');\n\n function search(\n items: FileSnapshot[],\n remainingParts: string[]\n ): FileSnapshot | null {\n for (const item of items) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (remainingParts.length === 1) {\n const target = remainingParts[0];\n if (\n itemNameLower === target ||\n itemTokenLower === target ||\n itemNameLower.replace(/\\.[^.]+$/, '') === target.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n }\n\n if (item.kind === 'folder' && item.children) {\n if (\n itemNameLower === remainingParts[0] ||\n itemTokenLower === remainingParts[0]\n ) {\n const result = search(item.children, remainingParts.slice(1));\n if (result) return result;\n }\n\n const result = search(item.children, remainingParts);\n if (result) return result;\n }\n }\n\n return null;\n }\n\n let result = search(files, pathParts);\n if (result) return result;\n\n result = searchByName(files, pathParts[pathParts.length - 1]);\n return result;\n}\n\nfunction searchByName(\n files: FileSnapshot[],\n name: string\n): FileSnapshot | null {\n const nameLower = name.toLowerCase();\n\n for (const item of files) {\n const itemNameLower = item.name.toLowerCase();\n const itemTokenLower = item.token.toLowerCase();\n\n if (\n itemNameLower === nameLower ||\n itemTokenLower === nameLower ||\n itemNameLower.replace(/\\.[^.]+$/, '') === nameLower.replace(/\\.[^.]+$/, '')\n ) {\n return item;\n }\n\n if (item.kind === 'folder' && item.children) {\n const result = searchByName(item.children, name);\n if (result) return result;\n }\n }\n\n return null;\n}\n","/**\n * MCP File Operations Tools\n *\n * Provides tools for AI to read, write, create, delete, and search files in books.\n */\n\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SlimaApiClient } from '../api/client.js';\nimport { formatErrorForMcp } from '../utils/errors.js';\n\n/**\n * Register file operation tools\n */\nexport function registerFileTools(\n server: McpServer,\n client: SlimaApiClient\n): void {\n // === read_file ===\n server.tool(\n 'read_file',\n 'Read the content of any file in a book by its path',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\" or \"chapter-01.md\")'),\n },\n async ({ book_token, path }) => {\n try {\n const result = await client.readFile(book_token, path);\n\n const fileInfo = [\n `# ${result.file.name}`,\n '',\n `**Path:** ${result.file.path}`,\n `**Words:** ${result.file.wordCount}`,\n `**Type:** ${result.file.fileType || 'unknown'}`,\n '',\n '---',\n '',\n result.content || '(empty file)',\n ].join('\\n');\n\n return {\n content: [{ type: 'text', text: fileInfo }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === write_file ===\n server.tool(\n 'write_file',\n 'Replace the entire content of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n content: z.string().describe('New content to write'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.updateFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File updated successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === edit_file ===\n server.tool(\n 'edit_file',\n 'Edit a file by replacing a specific text segment (search and replace). Use this for precise edits instead of rewriting the entire file.',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"characters/protagonist.md\")'),\n old_string: z.string().describe('The exact text to find and replace (must match exactly, including whitespace)'),\n new_string: z.string().describe('The new text to replace it with'),\n replace_all: z.boolean().optional().describe('Replace all occurrences (default: false, replaces only first match)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, old_string, new_string, replace_all, commit_message }) => {\n try {\n // 1. Read current content\n const fileData = await client.readFile(book_token, path);\n const currentContent = fileData.content || '';\n\n // 2. Check if old_string exists\n if (!currentContent.includes(old_string)) {\n return {\n content: [\n {\n type: 'text',\n text: `❌ Edit failed: Could not find the specified text in the file.\\n\\n**Looking for:**\\n\\`\\`\\`\\n${old_string.slice(0, 200)}${old_string.length > 200 ? '...' : ''}\\n\\`\\`\\`\\n\\n**Tip:** Make sure the text matches exactly, including whitespace and line breaks.`,\n },\n ],\n isError: true,\n };\n }\n\n // 3. Check for uniqueness (if not replace_all)\n if (!replace_all) {\n const occurrences = currentContent.split(old_string).length - 1;\n if (occurrences > 1) {\n return {\n content: [\n {\n type: 'text',\n text: `❌ Edit failed: Found ${occurrences} occurrences of the text. Please provide a more specific/longer text segment to ensure a unique match, or set replace_all: true to replace all occurrences.`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // 4. Perform replacement\n const newContent = replace_all\n ? currentContent.split(old_string).join(new_string)\n : currentContent.replace(old_string, new_string);\n\n // 5. Write back\n const result = await client.updateFile(book_token, {\n path,\n content: newContent,\n commitMessage: commit_message || `Edit ${path}`,\n });\n\n const replacementCount = replace_all\n ? currentContent.split(old_string).length - 1\n : 1;\n\n return {\n content: [\n {\n type: 'text',\n text: `✅ File edited successfully.\\n\\n**Replacements:** ${replacementCount}\\n**Commit:** ${result.commit.token}\\n**Message:** ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === create_file ===\n server.tool(\n 'create_file',\n 'Create a new file in a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path including parent folders (e.g., \"characters/new-character.md\")'),\n content: z.string().optional().describe('Initial content (optional, defaults to empty)'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.createFile(book_token, {\n path,\n content: content || '',\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File created successfully.\\n\\nFile Token: ${result.fileToken}\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === delete_file ===\n server.tool(\n 'delete_file',\n 'Delete a file from a book (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path to delete (e.g., \"old-notes.md\")'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, commit_message }) => {\n try {\n const result = await client.deleteFile(book_token, {\n path,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `File deleted successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === append_to_file ===\n server.tool(\n 'append_to_file',\n 'Append content to the end of an existing file (creates a new commit)',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n path: z.string().describe('File path (e.g., \"notes.md\")'),\n content: z.string().describe('Content to append'),\n commit_message: z.string().optional().describe('Commit message (optional, auto-generated if not provided)'),\n },\n async ({ book_token, path, content, commit_message }) => {\n try {\n const result = await client.appendToFile(book_token, {\n path,\n content,\n commitMessage: commit_message,\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `Content appended successfully.\\n\\nCommit: ${result.commit.token}\\nMessage: ${result.commit.name || result.commit.message}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n\n // === search_content ===\n server.tool(\n 'search_content',\n 'Search for content across all files in a book',\n {\n book_token: z.string().describe('Book token (e.g., bk_abc123)'),\n query: z.string().describe('Search query (e.g., \"blue eyes\" or \"protagonist name\")'),\n file_types: z.array(z.string()).optional().describe('Filter by file types (e.g., [\"markdown\", \"txt\"])'),\n limit: z.number().optional().describe('Maximum number of matches to return (default: 20)'),\n },\n async ({ book_token, query, file_types, limit }) => {\n try {\n const result = await client.searchFiles(book_token, {\n query,\n fileTypes: file_types,\n limit,\n });\n\n if (result.matches.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: `No matches found for \"${query}\".`,\n },\n ],\n };\n }\n\n const output: string[] = [\n `# Search Results for \"${query}\"`,\n '',\n `Found ${result.matches.length} file(s) with matches:`,\n '',\n ];\n\n for (const match of result.matches) {\n output.push(`## ${match.file.path}`);\n output.push(`Words: ${match.file.wordCount} | Matches: ${match.matchCount}`);\n output.push('');\n\n for (const snippet of match.snippets) {\n output.push(`> ${snippet.text}`);\n output.push('');\n }\n }\n\n return {\n content: [{ type: 'text', text: output.join('\\n') }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: formatErrorForMcp(error) }],\n isError: true,\n };\n }\n }\n );\n}\n","/**\n * Logger 工具\n *\n * 重要:MCP STDIO 模式下,stdout 是協議通道,\n * 所有日誌必須輸出到 stderr\n */\n\nconst isDebug = process.env.DEBUG === 'true' || process.env.DEBUG === '1';\n\nexport const logger = {\n info: (message: string, ...args: unknown[]) => {\n console.error(`[INFO] ${message}`, ...args);\n },\n\n warn: (message: string, ...args: unknown[]) => {\n console.error(`[WARN] ${message}`, ...args);\n },\n\n error: (message: string, error?: unknown) => {\n if (error instanceof Error) {\n console.error(`[ERROR] ${message}:`, error.message);\n if (isDebug && error.stack) {\n console.error(error.stack);\n }\n } else if (error !== undefined) {\n console.error(`[ERROR] ${message}:`, error);\n } else {\n console.error(`[ERROR] ${message}`);\n }\n },\n\n debug: (message: string, ...args: unknown[]) => {\n if (isDebug) {\n console.error(`[DEBUG] ${message}`, ...args);\n }\n },\n};\n","/**\n * Token Storage Utility (CLI)\n *\n * Stores API Token in user's home directory\n * ~/.slima/credentials.json\n */\n\nimport { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst SLIMA_DIR = '.slima';\nconst CREDENTIALS_FILE = 'credentials.json';\n\ninterface Credentials {\n apiToken: string;\n createdAt: string;\n baseUrl?: string;\n}\n\nfunction getCredentialsPath(): string {\n return join(homedir(), SLIMA_DIR, CREDENTIALS_FILE);\n}\n\nfunction getSlimaDir(): string {\n return join(homedir(), SLIMA_DIR);\n}\n\n/**\n * Save API Token\n */\nexport async function saveToken(apiToken: string, baseUrl?: string): Promise<void> {\n const slimaDir = getSlimaDir();\n\n // Ensure directory exists\n await fs.mkdir(slimaDir, { recursive: true });\n\n const credentials: Credentials = {\n apiToken,\n createdAt: new Date().toISOString(),\n ...(baseUrl && { baseUrl }),\n };\n\n const credentialsPath = getCredentialsPath();\n await fs.writeFile(credentialsPath, JSON.stringify(credentials, null, 2), 'utf-8');\n\n // Set file permission to 600 (owner only)\n await fs.chmod(credentialsPath, 0o600);\n}\n\n/**\n * Load API Token\n */\nexport async function loadToken(): Promise<Credentials | null> {\n try {\n const credentialsPath = getCredentialsPath();\n const content = await fs.readFile(credentialsPath, 'utf-8');\n return JSON.parse(content) as Credentials;\n } catch {\n return null;\n }\n}\n\n/**\n * Delete API Token\n */\nexport async function deleteToken(): Promise<void> {\n try {\n const credentialsPath = getCredentialsPath();\n await fs.unlink(credentialsPath);\n } catch {\n // File doesn't exist, ignore\n }\n}\n\n/**\n * Check if authenticated\n */\nexport async function isAuthenticated(): Promise<boolean> {\n const credentials = await loadToken();\n return credentials !== null && !!credentials.apiToken;\n}\n\n/**\n * Get credentials file path (for display)\n */\nexport function getCredentialsFilePath(): string {\n return getCredentialsPath();\n}\n","/**\n * CLI Auth Command\n *\n * 實作類似 Claude Code / GitHub CLI 的認證流程:\n * 1. 使用者執行 `slima-mcp auth`\n * 2. 開啟瀏覽器到 Slima 認證頁面\n * 3. 使用者登入並授權\n * 4. 本地伺服器接收 callback 並儲存 token\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { URL } from 'node:url';\nimport { randomBytes } from 'node:crypto';\nimport { saveToken, loadToken, deleteToken, getCredentialsFilePath } from './token-storage.js';\n\n// 認證使用 API 伺服器(處理 OAuth 和 session)\nconst DEFAULT_API_URL = 'https://api.slima.ai';\nconst CALLBACK_PORT_START = 8765;\nconst CALLBACK_PORT_END = 8775;\n\n/**\n * 開啟瀏覽器\n */\nasync function openBrowser(url: string): Promise<void> {\n const { exec } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const execAsync = promisify(exec);\n\n const platform = process.platform;\n\n try {\n if (platform === 'darwin') {\n await execAsync(`open \"${url}\"`);\n } else if (platform === 'win32') {\n await execAsync(`start \"\" \"${url}\"`);\n } else {\n // Linux / WSL\n // 嘗試多個可能的命令\n const commands = ['xdg-open', 'sensible-browser', 'wslview'];\n for (const cmd of commands) {\n try {\n await execAsync(`${cmd} \"${url}\"`);\n return;\n } catch {\n // 繼續嘗試下一個\n }\n }\n // 如果都失敗,顯示 URL 讓使用者手動開啟\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n } catch {\n console.log(`\\nPlease open this URL in your browser:\\n${url}\\n`);\n }\n}\n\n/**\n * 尋找可用的 port\n */\nasync function findAvailablePort(): Promise<number> {\n for (let port = CALLBACK_PORT_START; port <= CALLBACK_PORT_END; port++) {\n const available = await isPortAvailable(port);\n if (available) {\n return port;\n }\n }\n throw new Error(`No available ports between ${CALLBACK_PORT_START} and ${CALLBACK_PORT_END}`);\n}\n\nfunction isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once('error', () => resolve(false));\n server.once('listening', () => {\n server.close(() => resolve(true));\n });\n server.listen(port, '127.0.0.1');\n });\n}\n\n/**\n * 產生隨機 state(防止 CSRF)\n */\nfunction generateState(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * 成功頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getSuccessHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Welcome to Slima</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .success-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .success-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .subtitle {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .footer {\n font-size: 12px;\n color: #86868B;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"success-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\" />\n </svg>\n </div>\n <h1>Welcome to Slima</h1>\n <p class=\"subtitle\">Authentication successful. Your CLI is now connected.</p>\n <p class=\"hint\">You can close this window and return to your terminal.</p>\n <div class=\"divider\"></div>\n <p class=\"footer\">Slima MCP is ready to use</p>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 錯誤頁面 HTML\n */\n/**\n * HTML escape to prevent XSS\n */\nfunction escapeHtml(text: string): string {\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;',\n };\n return text.replace(/[&<>\"']/g, (char) => map[char]);\n}\n\n/**\n * 錯誤頁面 HTML\n * Design follows UIUX_SPEC.md: minimalist, grayscale, Inter font\n */\nfunction getErrorHtml(message: string): string {\n const safeMessage = escapeHtml(message);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>Slima - Authentication Error</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n background: #FBFBFA;\n color: #1D1D1F;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .container {\n text-align: center;\n padding: 48px;\n max-width: 420px;\n }\n .logo {\n width: 48px;\n height: 48px;\n margin-bottom: 32px;\n opacity: 0.9;\n }\n .error-icon {\n width: 64px;\n height: 64px;\n margin-bottom: 24px;\n border-radius: 50%;\n background: #1D1D1F;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: auto;\n margin-right: auto;\n }\n .error-icon svg {\n width: 32px;\n height: 32px;\n stroke: #FBFBFA;\n }\n h1 {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin-bottom: 12px;\n color: #1D1D1F;\n }\n .message {\n font-size: 15px;\n color: #6E6E73;\n line-height: 1.5;\n margin-bottom: 8px;\n }\n .hint {\n font-size: 13px;\n color: #86868B;\n margin-top: 24px;\n }\n .divider {\n width: 40px;\n height: 1px;\n background: #E5E5E5;\n margin: 32px auto;\n }\n .retry-link {\n display: inline-block;\n font-size: 14px;\n font-weight: 500;\n color: #1D1D1F;\n text-decoration: none;\n padding: 10px 20px;\n border: 1px solid #E5E5E5;\n border-radius: 8px;\n transition: all 0.2s ease;\n }\n .retry-link:hover {\n background: #F5F5F5;\n border-color: #D5D5D5;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <img src=\"https://app.slima.ai/icons/slima-black.svg\" alt=\"Slima\" class=\"logo\">\n <div class=\"error-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </div>\n <h1>Something went wrong</h1>\n <p class=\"message\">${safeMessage}</p>\n <p class=\"hint\">Please close this window and try again from your terminal.</p>\n <div class=\"divider\"></div>\n <a href=\"javascript:window.close()\" class=\"retry-link\">Close Window</a>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * 執行認證流程\n * @param options.baseUrl - API base URL (default: https://api.slima.ai)\n * @param options.force - Force re-authentication even if already authenticated\n */\nexport async function runAuth(options: { baseUrl?: string; force?: boolean } = {}): Promise<void> {\n const baseUrl = options.baseUrl || process.env.SLIMA_API_URL || DEFAULT_API_URL;\n const forceAuth = options.force || false;\n\n console.log('\\n🔐 Slima MCP Authentication\\n');\n\n // 檢查是否已經認證\n const existingCredentials = await loadToken();\n if (existingCredentials && !forceAuth) {\n console.log('You are already authenticated.');\n console.log(`Token stored at: ${getCredentialsFilePath()}`);\n console.log('\\nTo re-authenticate with a different account, run: slima-mcp auth --force\\n');\n return;\n }\n\n // 如果強制認證,先刪除舊 token\n if (existingCredentials && forceAuth) {\n await deleteToken();\n console.log('Clearing existing credentials for re-authentication...\\n');\n }\n\n const state = generateState();\n const port = await findAvailablePort();\n\n console.log('Starting local server for authentication callback...');\n\n // 建立本地伺服器接收 callback\n const tokenPromise = new Promise<string>((resolve, reject) => {\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n const token = url.searchParams.get('token');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(error));\n server.close();\n reject(new Error(error));\n return;\n }\n\n if (returnedState !== state) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('Invalid state parameter. Please try again.'));\n server.close();\n reject(new Error('State mismatch'));\n return;\n }\n\n if (!token) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('No token received. Please try again.'));\n server.close();\n reject(new Error('No token received'));\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessHtml());\n server.close();\n resolve(token);\n } else {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n server.listen(port, '127.0.0.1', () => {\n console.log(`Listening on http://127.0.0.1:${port}`);\n });\n\n // 30 秒超時\n setTimeout(() => {\n server.close();\n reject(new Error('Authentication timed out. Please try again.'));\n }, 30000);\n });\n\n // 建立認證 URL(使用 API endpoint)\n // 加入 prompt=login 強制重新登入(清除 Rails session)\n const promptParam = forceAuth ? '&prompt=login' : '';\n const authUrl = `${baseUrl}/api/v1/auth/cli?state=${state}&callback_port=${port}${promptParam}`;\n\n console.log('\\nOpening browser for authentication...');\n await openBrowser(authUrl);\n\n console.log('\\nWaiting for authentication...');\n console.log('(Press Ctrl+C to cancel)\\n');\n\n try {\n const token = await tokenPromise;\n await saveToken(token, baseUrl !== DEFAULT_API_URL ? baseUrl : undefined);\n\n console.log('\\n✅ Authentication successful!');\n console.log(`Token saved to: ${getCredentialsFilePath()}`);\n console.log('\\nYou can now use Slima MCP with any MCP-compatible AI tool.\\n');\n } catch (error) {\n console.error('\\n❌ Authentication failed:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\n/**\n * 登出(刪除 token)\n */\nexport async function runLogout(): Promise<void> {\n console.log('\\n🔓 Slima MCP Logout\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('You are not authenticated.\\n');\n return;\n }\n\n await deleteToken();\n console.log('✅ Successfully logged out.');\n console.log(`Removed credentials from: ${getCredentialsFilePath()}\\n`);\n}\n\n/**\n * 顯示目前狀態\n */\nexport async function runStatus(): Promise<void> {\n console.log('\\n📊 Slima MCP Status\\n');\n\n const credentials = await loadToken();\n if (!credentials) {\n console.log('Status: Not authenticated');\n console.log('\\nRun `slima-mcp auth` to authenticate.\\n');\n return;\n }\n\n console.log('Status: Authenticated');\n console.log(`Token: ${credentials.apiToken.substring(0, 15)}...`);\n console.log(`Authenticated at: ${credentials.createdAt}`);\n if (credentials.baseUrl) {\n console.log(`API URL: ${credentials.baseUrl}`);\n }\n console.log(`Credentials file: ${getCredentialsFilePath()}\\n`);\n}\n"],"mappings":"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","names":["z","z","result","z","defaultLogger","logger","z","findFileInSnapshot","result","searchByName","z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slima-mcp",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "MCP Server for Slima - AI Writing IDE for Novel Authors. Works with Claude, ChatGPT, Gemini, Cursor, and any MCP-compatible AI tool.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",