joplin-mcp-server 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["crypto"],"sources":["../src/lib/joplin-sidecar.ts","../src/lib/parse-args.ts","../src/lib/tools/base-tool.ts","../src/lib/tools/create-folder.ts","../src/lib/tools/create-note.ts","../src/lib/tools/delete-folder.ts","../src/lib/tools/delete-note.ts","../src/lib/tools/edit-folder.ts","../src/lib/tools/edit-note.ts","../src/lib/tools/list-notebooks.ts","../src/lib/tools/read-multi-note.ts","../src/lib/tools/read-note.ts","../src/lib/tools/read-notebook.ts","../src/lib/tools/search-notes.ts","../src/lib/joplin-api-client.ts","../src/server-core.ts","../src/server-fastmcp.ts","../src/index.ts"],"sourcesContent":["import { type ChildProcess, exec, execFile, spawn } from \"child_process\"\nimport crypto from \"crypto\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport { Platform } from \"functype-os\"\nimport { join } from \"path\"\nimport { promisify } from \"util\"\n\nconst execAsync = promisify(exec)\nconst execFileAsync = promisify(execFile)\n\nconst isWindows = Platform.isWindows()\nconst whichCmd = isWindows ? \"where\" : \"which\"\n\nexport const DEFAULT_API_PORT = 41184\nconst MAX_PORT_ATTEMPTS = 10\n\nexport type SyncTarget =\n | { type: \"none\" }\n | { type: \"filesystem\"; path: string }\n | { type: \"joplin-cloud\"; email: string; password: string }\n | { type: \"joplin-server\"; url: string; email: string; password: string }\n | { type: \"webdav\"; url: string; username: string; password: string }\n | { type: \"nextcloud\"; url: string; username: string; password: string }\n | { type: \"s3\"; bucket: string; region: string; accessKey: string; secretKey: string }\n | { type: \"dropbox\" }\n | { type: \"onedrive\" }\n\nexport type SidecarConfig = {\n profileDir: string\n apiPort: number\n apiToken: string\n syncTarget?: SyncTarget\n syncInterval?: number\n}\n\nexport type SidecarError = {\n code:\n | \"CLI_NOT_FOUND\"\n | \"CONFIG_FAILED\"\n | \"SPAWN_FAILED\"\n | \"HEALTH_CHECK_FAILED\"\n | \"STOP_FAILED\"\n | \"SYNC_FAILED\"\n | \"PORT_CONFLICT\"\n | \"PORT_OCCUPIED\"\n | \"PORT_EXHAUSTED\"\n message: string\n cause?: unknown\n}\n\nconst sidecarError = (code: SidecarError[\"code\"], message: string, cause?: unknown): SidecarError => ({\n code,\n message,\n cause,\n})\n\nconst syncTargetId = (target: SyncTarget): number =>\n Match(target.type)\n .case(\"none\", () => 0)\n .case(\"filesystem\", () => 2)\n .case(\"webdav\", () => 6)\n .case(\"nextcloud\", () => 5)\n .case(\"dropbox\", () => 7)\n .case(\"onedrive\", () => 3)\n .case(\"s3\", () => 8)\n .case(\"joplin-server\", () => 9)\n .case(\"joplin-cloud\", () => 10)\n .default(() => 0)\n\nconst fetchWithTimeout = async (url: string, timeoutMs: number = 5_000): Promise<Response> => {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { signal: controller.signal })\n } finally {\n clearTimeout(timer)\n }\n}\n\ntype PortProbeResult = \"free\" | \"joplin_ours\" | \"joplin_foreign\" | \"occupied_other\" | \"occupied_unresponsive\"\n\nconst isConnectionRefused = (err: unknown): boolean => {\n const e = err as { cause?: { code?: string; errors?: Array<{ code?: string }> } }\n if (e.cause?.code === \"ECONNREFUSED\") return true\n if (e.cause?.errors?.some((inner) => inner.code === \"ECONNREFUSED\") === true) return true\n return false\n}\n\nconst probePort = async (port: number, token: string): Promise<PortProbeResult> => {\n try {\n const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`, 3_000)\n const body = await pingResponse.text()\n if (body !== \"JoplinClipperServer\") return \"occupied_other\"\n\n // It's a Joplin server — check if the token matches\n try {\n const authResponse = await fetchWithTimeout(\n `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n 3_000,\n )\n if (authResponse.ok) return \"joplin_ours\"\n return \"joplin_foreign\"\n } catch {\n return \"joplin_foreign\"\n }\n } catch (err: unknown) {\n // ECONNREFUSED = nothing listening on the port = genuinely free\n if (isConnectionRefused(err)) return \"free\"\n // Timeout or other error = port is occupied but not responding to HTTP\n return \"occupied_unresponsive\"\n }\n}\n\ntype PortResolution =\n | { outcome: \"reuse_existing\"; port: number; desktopDetected: boolean }\n | { outcome: \"free\"; port: number; desktopDetected: boolean }\n | { outcome: \"exhausted\" }\n\nconst resolveAvailablePort = async (startPort: number, token: string): Promise<PortResolution> => {\n const tryPort = async (port: number, desktopDetected: boolean): Promise<PortResolution> => {\n if (port >= startPort + MAX_PORT_ATTEMPTS) return { outcome: \"exhausted\" }\n const status = await probePort(port, token)\n if (status === \"free\") return { outcome: \"free\", port, desktopDetected }\n if (status === \"joplin_ours\") return { outcome: \"reuse_existing\", port, desktopDetected }\n if (status === \"joplin_foreign\") {\n process.stderr.write(`[joplin-sidecar] Port ${port}: Joplin Desktop detected (different token), skipping\\n`)\n return tryPort(port + 1, true)\n }\n process.stderr.write(`[joplin-sidecar] Port ${port} occupied (${status}), trying next...\\n`)\n return tryPort(port + 1, desktopDetected)\n }\n return tryPort(startPort, false)\n}\n\nconst findJoplinCli = async (): Promise<Either<SidecarError, string>> => {\n // 1. User override via env var\n const envCli = process.env.JOPLIN_CLI\n if (envCli) {\n if (fs.existsSync(envCli)) return Right(envCli)\n return Left(sidecarError(\"CLI_NOT_FOUND\", `JOPLIN_CLI path not found: ${envCli}`))\n }\n\n // 2. Bundled in node_modules (if joplin is a dependency)\n const localBin = join(process.cwd(), \"node_modules\", \".bin\", isWindows ? \"joplin.cmd\" : \"joplin\")\n if (fs.existsSync(localBin)) return Right(localBin)\n\n // 3. Global install\n try {\n const { stdout } = await execAsync(`${whichCmd} joplin`, { encoding: \"utf-8\", timeout: 10_000 })\n const joplinPath = stdout.trim().split(\"\\n\")[0]\n return Right(joplinPath)\n } catch {\n // not found\n }\n\n // 4. npx fallback (auto-downloads on first run)\n try {\n const { stdout } = await execAsync(`${whichCmd} npx`, { encoding: \"utf-8\", timeout: 10_000 })\n const npxPath = stdout.trim().split(\"\\n\")[0]\n process.stderr.write(\"[joplin-sidecar] No local joplin found, using npx (may download on first run)\\n\")\n return Right(npxPath)\n } catch {\n // not found\n }\n\n return Left(\n sidecarError(\n \"CLI_NOT_FOUND\",\n \"Joplin CLI not found. Install with: npm install -g joplin, or set JOPLIN_CLI=/path/to/joplin\",\n ),\n )\n}\n\nconst buildSettingsRecord = (config: SidecarConfig): Record<string, string> => {\n const settings: Record<string, string> = {\n \"api.token\": config.apiToken,\n \"api.port\": String(config.apiPort),\n }\n\n const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n settings[\"sync.target\"] = String(syncTargetId(syncTarget))\n\n if (syncTarget.type === \"filesystem\") {\n settings[\"sync.2.path\"] = syncTarget.path\n } else if (syncTarget.type === \"webdav\") {\n settings[\"sync.6.path\"] = syncTarget.url\n settings[\"sync.6.username\"] = syncTarget.username\n settings[\"sync.6.password\"] = syncTarget.password\n } else if (syncTarget.type === \"nextcloud\") {\n settings[\"sync.5.path\"] = syncTarget.url\n settings[\"sync.5.username\"] = syncTarget.username\n settings[\"sync.5.password\"] = syncTarget.password\n } else if (syncTarget.type === \"s3\") {\n settings[\"sync.8.path\"] = syncTarget.bucket\n settings[\"sync.8.region\"] = syncTarget.region\n settings[\"sync.8.username\"] = syncTarget.accessKey\n settings[\"sync.8.password\"] = syncTarget.secretKey\n } else if (syncTarget.type === \"joplin-server\") {\n settings[\"sync.9.path\"] = syncTarget.url\n settings[\"sync.9.username\"] = syncTarget.email\n settings[\"sync.9.password\"] = syncTarget.password\n } else if (syncTarget.type === \"joplin-cloud\") {\n settings[\"sync.10.username\"] = syncTarget.email\n settings[\"sync.10.password\"] = syncTarget.password\n }\n\n const interval = Option(config.syncInterval).orElse(300)\n settings[\"sync.interval\"] = String(interval)\n\n return settings\n}\n\nconst runJoplinConfig = async (cli: string, profileDir: string, key: string, value: string): Promise<void> => {\n const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n const args = cli.endsWith(\"npx\")\n ? [\"joplin\", \"config\", \"--profile\", profileDir, key, value]\n : [\"config\", \"--profile\", profileDir, key, value]\n await execFileAsync(cmd, args, { encoding: \"utf-8\", timeout: 30_000, shell: isWindows })\n}\n\nconst configureJoplin = async (cli: string, config: SidecarConfig): Promise<Either<SidecarError, void>> => {\n const settings = buildSettingsRecord(config)\n try {\n fs.mkdirSync(config.profileDir, { recursive: true })\n for (const [key, value] of Object.entries(settings)) {\n await runJoplinConfig(cli, config.profileDir, key, value)\n }\n return Right(undefined as void)\n } catch (e) {\n return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to configure Joplin via CLI\", e))\n }\n}\n\nconst spawnServer = (cli: string, config: SidecarConfig): Either<SidecarError, ChildProcess> => {\n try {\n const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n const args = cli.endsWith(\"npx\")\n ? [\"joplin\", \"server\", \"start\", \"--profile\", config.profileDir]\n : [\"server\", \"start\", \"--profile\", config.profileDir]\n\n const proc = spawn(cmd, args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: false,\n shell: isWindows,\n })\n\n proc.stderr.on(\"data\", (data: Buffer) => {\n process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n })\n\n proc.stdout.on(\"data\", (data: Buffer) => {\n process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n })\n\n proc.on(\"error\", (err) => {\n process.stderr.write(`[joplin-sidecar] Process error: ${err.message}\\n`)\n })\n\n return Right(proc as ChildProcess)\n } catch (e) {\n return Left(sidecarError(\"SPAWN_FAILED\", \"Failed to spawn Joplin server process\", e))\n }\n}\n\nconst waitForReady = async (\n port: number,\n token: string,\n proc: ChildProcess | null,\n maxRetries: number = 30,\n intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n const deadline = Date.now() + 60_000\n\n // Track if the spawned process exits early (e.g., port already taken).\n // Mutable object container keeps the binding `const` while allowing the\n // exit handler to record the code asynchronously.\n const procExitState: { code: number | null } = { code: null }\n if (proc) {\n proc.once(\"exit\", (code) => {\n procExitState.code = code\n })\n }\n\n const attempt = async (i: number): Promise<Either<SidecarError, true>> => {\n if (i >= maxRetries || Date.now() > deadline) {\n return Left(sidecarError(\"HEALTH_CHECK_FAILED\", \"Joplin server did not become ready within 60s\"))\n }\n\n if (procExitState.code !== null) {\n return Left(\n sidecarError(\n \"SPAWN_FAILED\",\n `Joplin server process exited unexpectedly (code ${procExitState.code}). ` +\n `Port ${port} may already be in use by another Joplin instance or process.`,\n ),\n )\n }\n\n try {\n const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`)\n if (pingResponse.ok) {\n try {\n const authResponse = await fetchWithTimeout(\n `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n )\n if (authResponse.ok) {\n process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n return Right(true as const)\n }\n process.stderr.write(\n `[joplin-sidecar] Ping OK but auth failed (status ${authResponse.status}), retrying...\\n`,\n )\n } catch {\n process.stderr.write(\"[joplin-sidecar] Ping OK but auth check timed out, retrying...\\n\")\n }\n }\n } catch {\n // Not ready yet\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n return attempt(i + 1)\n }\n\n return attempt(0)\n}\n\nconst CONFIG_CACHE_FILE = \".mcp-configured\"\n\nconst computeConfigHash = (config: SidecarConfig): string => {\n const hashData = {\n apiPort: config.apiPort,\n apiToken: config.apiToken,\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n }\n return crypto.createHash(\"sha256\").update(JSON.stringify(hashData)).digest(\"hex\").slice(0, 16)\n}\n\nconst isConfigCached = (profileDir: string, hash: string): boolean => {\n try {\n const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n if (!fs.existsSync(cachePath)) return false\n const cached = JSON.parse(fs.readFileSync(cachePath, \"utf-8\"))\n return cached.hash === hash\n } catch {\n return false\n }\n}\n\nconst writeConfigCache = (profileDir: string, hash: string): void => {\n try {\n const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n fs.writeFileSync(cachePath, JSON.stringify({ hash, timestamp: Date.now() }))\n } catch {\n // Non-critical — skip silently\n }\n}\n\nexport class JoplinSidecar {\n private config: SidecarConfig\n private childProcess: ChildProcess | null = null\n private startPromise: Promise<Either<SidecarError, ChildProcess>> | null = null\n private portResolution: PortResolution | null = null\n\n constructor(config: Partial<SidecarConfig> & { apiToken: string }) {\n this.config = {\n profileDir: config.profileDir ?? join(Platform.homeDir(), \".config\", \"joplin-mcp\"),\n apiPort: config.apiPort ?? DEFAULT_API_PORT,\n apiToken: config.apiToken,\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n }\n }\n\n async resolvePort(): Promise<Either<SidecarError, number>> {\n const resolution = await resolveAvailablePort(this.config.apiPort, this.config.apiToken)\n this.portResolution = resolution\n if (resolution.outcome === \"exhausted\") {\n const lastPort = this.config.apiPort + MAX_PORT_ATTEMPTS - 1\n return Left(\n sidecarError(\n \"PORT_EXHAUSTED\",\n `All ports ${this.config.apiPort}-${lastPort} are occupied. Free a port or stop other Joplin instances.`,\n ),\n )\n }\n this.config.apiPort = resolution.port\n if (resolution.desktopDetected) {\n process.stderr.write(\n \"[joplin-sidecar] WARNING: Joplin Desktop is running. The sidecar uses a separate database.\\n\" +\n \"[joplin-sidecar] Notes will sync between them only if both are configured with the same sync target.\\n\",\n )\n }\n return Right(resolution.port)\n }\n\n isDesktopDetected(): boolean {\n return this.portResolution?.outcome !== \"exhausted\" && (this.portResolution?.desktopDetected ?? false)\n }\n\n async start(): Promise<Either<SidecarError, ChildProcess>> {\n if (this.startPromise) return this.startPromise\n this.startPromise = this.doStart()\n return this.startPromise\n }\n\n private async doStart(): Promise<Either<SidecarError, ChildProcess>> {\n // Step 1: Find CLI\n const cliResult = await findJoplinCli()\n if (Either.isLeft(cliResult)) {\n return Left(\n cliResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n const cli = cliResult.fold(\n () => \"\",\n (v) => v,\n )\n process.stderr.write(`[joplin-sidecar] Found CLI: ${cli}\\n`)\n\n // Step 2: Resolve port if not already done (defensive fallback)\n if (!this.portResolution) {\n const portResult = await this.resolvePort()\n if (Either.isLeft(portResult)) {\n return Left(\n portResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n }\n\n // Step 3: If we found an existing instance with our token, reuse it\n if (this.portResolution?.outcome === \"reuse_existing\") {\n process.stderr.write(\n `[joplin-sidecar] Existing Joplin server with matching token on port ${this.config.apiPort}, reusing\\n`,\n )\n return Right(this.childProcess ?? (null as unknown as ChildProcess))\n }\n\n // Step 4: Configure via CLI (skip if config is cached)\n const configHash = computeConfigHash(this.config)\n if (isConfigCached(this.config.profileDir, configHash)) {\n process.stderr.write(\"[joplin-sidecar] Configuration cached, skipping config step\\n\")\n } else {\n const configResult = await configureJoplin(cli, this.config)\n if (Either.isLeft(configResult)) {\n return Left(\n configResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n writeConfigCache(this.config.profileDir, configHash)\n process.stderr.write(\"[joplin-sidecar] Configuration applied via CLI\\n\")\n }\n\n // Step 5: Spawn server (port is free, skip if already spawned from a previous attempt)\n if (!this.childProcess) {\n const spawnResult = spawnServer(cli, this.config)\n if (Either.isLeft(spawnResult)) {\n return Left(\n spawnResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n const proc = spawnResult.fold(\n () => null as unknown as ChildProcess,\n (v) => v,\n )\n this.childProcess = proc\n process.stderr.write(`[joplin-sidecar] Server spawned (pid: ${proc.pid})\\n`)\n } else {\n process.stderr.write(\n `[joplin-sidecar] Server already spawned (pid: ${this.childProcess.pid}), waiting for ready\\n`,\n )\n }\n\n // Step 6: Wait for ready\n const readyResult = await waitForReady(this.config.apiPort, this.config.apiToken, this.childProcess)\n if (Either.isLeft(readyResult)) {\n return Left(\n readyResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n\n return Right(this.childProcess!)\n }\n\n async stop(): Promise<Either<Error, true>> {\n // Reset startPromise to allow retry via start() after stop()\n this.startPromise = null\n\n if (!this.childProcess) return Right(true as const)\n\n const proc = this.childProcess\n try {\n proc.kill(\"SIGTERM\")\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n proc.kill(\"SIGKILL\")\n resolve()\n }, 5000)\n\n proc.on(\"exit\", () => {\n clearTimeout(timeout)\n resolve()\n })\n })\n\n this.childProcess = null\n process.stderr.write(\"[joplin-sidecar] Server stopped\\n\")\n return Right(true as const)\n } catch (error) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async healthCheck(): Promise<Either<Error, true>> {\n try {\n const response = await fetchWithTimeout(`http://127.0.0.1:${this.config.apiPort}/ping`, 5_000)\n if (!response.ok) return Left(new Error(`Health check failed: ${response.status}`))\n return Right(true as const)\n } catch (error) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n getPort(): number {\n return this.config.apiPort\n }\n\n getHost(): string {\n return \"127.0.0.1\"\n }\n}\n","import fs from \"fs\"\nimport { Either, Left, Option, Right } from \"functype\"\nimport { Path, Platform } from \"functype-os\"\nimport { join, resolve } from \"path\"\n\nimport type { SyncTarget } from \"./joplin-sidecar.js\"\n\nexport type ParsedArgs = {\n remainingArgs: string[]\n transport: \"stdio\" | \"http\"\n httpPort: number\n profileDir: string\n syncTarget: Option<SyncTarget>\n}\n\n// Local expandVars preserves the existing \"silently substitute empty for missing\n// env vars\" behavior — functype-os's Path.expandVars returns Left on unresolved\n// variables, which would break paths like ~/${MAYBE_UNSET}/foo.\nconst expandVars = (p: string): string =>\n p\n .replace(/\\$\\{(\\w+)\\}/g, (_, name) => process.env[name] ?? \"\")\n .replace(/\\$(\\w+)/g, (_, name) => process.env[name] ?? \"\")\n\nconst isNonEmptyDir = (p: string): boolean => {\n try {\n const entries = fs.readdirSync(p)\n return entries.length > 0\n } catch {\n return false\n }\n}\n\n// On WSL, a path like ~/OneDrive may resolve to an empty Linux directory while\n// the real data lives under the Windows user profile. Fall back to the WSL\n// user's Windows home (resolved via cmd.exe %USERPROFILE%) when that happens.\nconst resolveWslPath = (linuxPath: string, relativeToHome: string): string => {\n if (!Platform.isWSL()) return linuxPath\n if (fs.existsSync(linuxPath) && isNonEmptyDir(linuxPath)) return linuxPath\n return Platform.windowsHomeDir()\n .map((winHome) => join(winHome, relativeToHome))\n .filter(isNonEmptyDir)\n .map((winPath) => {\n process.stderr.write(`[wsl] Path ${linuxPath} empty/missing, using Windows path: ${winPath}\\n`)\n return winPath\n })\n .orElse(linuxPath)\n}\n\nconst expandPath = (p: string): string => {\n const expanded = expandVars(p)\n const tildeExpanded = Path.expandTilde(expanded)\n if (expanded === \"~\" || expanded.startsWith(\"~/\")) {\n const relativeToHome = expanded === \"~\" ? \"\" : expanded.slice(2)\n return resolveWslPath(tildeExpanded, relativeToHome)\n }\n return tildeExpanded\n}\n\nconst extractArg = (args: string[], flag: string): Option<string> => {\n const index = args.indexOf(flag)\n if (index === -1) return Option.none()\n const value = args[index + 1]\n if (!value || value.startsWith(\"--\")) return Option.none()\n args.splice(index, 2)\n return Option(value)\n}\n\nexport const buildSyncTarget = (args: {\n syncTarget: Option<string>\n syncPath: Option<string>\n syncUsername: Option<string>\n syncPassword: Option<string>\n}): Either<string, SyncTarget> => {\n const targetType = args.syncTarget.orElse(\"none\")\n\n switch (targetType) {\n case \"none\":\n return Right({ type: \"none\" } as SyncTarget)\n\n case \"filesystem\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for filesystem sync target\"),\n (path) => Right({ type: \"filesystem\", path } as SyncTarget),\n )\n\n case \"webdav\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for webdav sync target\"),\n (url) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username required for webdav sync target\"),\n (username) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for webdav sync target\"),\n (password) => Right({ type: \"webdav\", url, username, password } as SyncTarget),\n ),\n ),\n )\n\n case \"nextcloud\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for nextcloud sync target\"),\n (url) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username required for nextcloud sync target\"),\n (username) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for nextcloud sync target\"),\n (password) => Right({ type: \"nextcloud\", url, username, password } as SyncTarget),\n ),\n ),\n )\n\n case \"joplin-cloud\":\n return args.syncUsername.fold(\n () => Left(\"--sync-username required for joplin-cloud sync target\"),\n (email) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for joplin-cloud sync target\"),\n (password) => Right({ type: \"joplin-cloud\", email, password } as SyncTarget),\n ),\n )\n\n case \"joplin-server\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for joplin-server sync target\"),\n (url) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username required for joplin-server sync target\"),\n (email) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for joplin-server sync target\"),\n (password) => Right({ type: \"joplin-server\", url, email, password } as SyncTarget),\n ),\n ),\n )\n\n case \"s3\":\n return args.syncPath.fold(\n () => Left(\"--sync-path (bucket) required for s3 sync target\"),\n (bucket) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username (access key) required for s3 sync target\"),\n (accessKey) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password (secret key) required for s3 sync target\"),\n (secretKey) => Right({ type: \"s3\", bucket, region: \"us-east-1\", accessKey, secretKey } as SyncTarget),\n ),\n ),\n )\n\n case \"dropbox\":\n return Right({ type: \"dropbox\" } as SyncTarget)\n\n case \"onedrive\":\n return Right({ type: \"onedrive\" } as SyncTarget)\n\n default:\n return Left(\n `Unknown sync target: ${targetType}. Valid targets: none, filesystem, webdav, nextcloud, joplin-cloud, joplin-server, s3, dropbox, onedrive`,\n )\n }\n}\n\nfunction parseArgs(): ParsedArgs {\n const args = process.argv.slice(2)\n\n // Load environment variables without dotenv debug output (for MCP stdio compatibility)\n const loadEnvFile = (envPath: string) => {\n try {\n if (fs.existsSync(envPath)) {\n process.stderr.write(`Loading environment from: ${envPath}\\n`)\n const envContent = fs.readFileSync(envPath, \"utf-8\")\n const envLines = envContent.split(\"\\n\")\n const loadedVars: string[] = []\n\n for (const line of envLines) {\n const trimmedLine = line.trim()\n if (trimmedLine && !trimmedLine.startsWith(\"#\")) {\n const [key, ...valueParts] = trimmedLine.split(\"=\")\n if (key && valueParts.length > 0) {\n const value = valueParts.join(\"=\").replace(/^[\"']|[\"']$/g, \"\")\n if (!process.env[key.trim()]) {\n process.env[key.trim()] = value\n loadedVars.push(key.trim())\n }\n }\n }\n }\n\n if (loadedVars.length > 0) {\n process.stderr.write(`Loaded variables: ${loadedVars.join(\", \")}\\n`)\n }\n }\n } catch (error: unknown) {\n process.stderr.write(`Error loading environment file: ${error}\\n`)\n }\n }\n\n // Handle --env-file\n const envFile = extractArg(args, \"--env-file\")\n envFile.fold(\n () => loadEnvFile(\".env\"),\n (file) => loadEnvFile(resolve(process.cwd(), file)),\n )\n\n // Handle --token\n extractArg(args, \"--token\").fold(\n () => {},\n (token) => {\n process.env.JOPLIN_TOKEN = token\n },\n )\n\n // Handle --transport\n const transport: \"stdio\" | \"http\" = extractArg(args, \"--transport\")\n .map((value): \"stdio\" | \"http\" => {\n if (value !== \"stdio\" && value !== \"http\") {\n process.stderr.write(\"Error: --transport must be either 'stdio' or 'http'\\n\")\n process.exit(1)\n }\n return value\n })\n .orElse(\"stdio\")\n\n // Handle --http-port\n const httpPort: number = extractArg(args, \"--http-port\")\n .map((value) => {\n const parsed = parseInt(value, 10)\n if (isNaN(parsed) || parsed < 1 || parsed > 65535) {\n process.stderr.write(\"Error: --http-port must be a valid port number (1-65535)\\n\")\n process.exit(1)\n }\n return parsed\n })\n .orElse(3000)\n\n // Handle --profile\n const profileDir = extractArg(args, \"--profile\")\n .or(Option(process.env.JOPLIN_PROFILE))\n .map(expandPath)\n .orElse(expandPath(\"~/.config/joplin-mcp\"))\n\n // Handle sync args\n const syncTarget = extractArg(args, \"--sync-target\").or(Option(process.env.JOPLIN_SYNC_TARGET))\n const syncPath = extractArg(args, \"--sync-path\").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath)\n const syncUsername = extractArg(args, \"--sync-username\").or(Option(process.env.JOPLIN_SYNC_USERNAME))\n const syncPassword = extractArg(args, \"--sync-password\").or(Option(process.env.JOPLIN_SYNC_PASSWORD))\n\n // Build and validate sync target\n const syncResult = buildSyncTarget({ syncTarget, syncPath, syncUsername, syncPassword })\n if (Either.isLeft(syncResult)) {\n const err = syncResult.fold(\n (e) => e,\n () => \"\",\n )\n process.stderr.write(`Error: ${err}\\n`)\n process.exit(1)\n }\n const syncTargetValue = syncResult.fold(\n () => ({ type: \"none\" }) as SyncTarget,\n (v) => v,\n )\n const resolvedSyncTarget: Option<SyncTarget> =\n syncTargetValue.type === \"none\" ? Option.none<SyncTarget>() : Option(syncTargetValue as SyncTarget)\n\n // Handle --help\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n process.stderr.write(`\nJoplin MCP Server (Sidecar Mode)\n\nUSAGE:\n joplin-mcp-server [OPTIONS]\n\nOPTIONS:\n --env-file <file> Load environment variables from file\n --token <token> Joplin API token\n --transport <type> Transport type: stdio (default) or http\n --http-port <port> HTTP server port (default: 3000, only with --transport http)\n --profile <dir> Joplin data directory (default: ~/.config/joplin-mcp)\n --sync-target <type> Sync target: none, filesystem, webdav, nextcloud,\n joplin-cloud, joplin-server, s3, dropbox, onedrive\n --sync-path <url> URL or path for sync target\n --sync-username <user> Username/email for sync\n --sync-password <pass> Password for sync\n --help, -h Show this help message\n\nENVIRONMENT VARIABLES:\n JOPLIN_TOKEN Joplin API token (required)\n JOPLIN_HOST Connect to existing Joplin at this host (skips sidecar)\n JOPLIN_PORT Connect to existing Joplin on this port (skips sidecar)\n JOPLIN_CLI Path to joplin CLI binary (overrides auto-detection)\n JOPLIN_PROFILE Joplin data directory\n JOPLIN_SYNC_TARGET Sync target type\n JOPLIN_SYNC_PATH Sync target URL/path\n JOPLIN_SYNC_USERNAME Sync username/email\n JOPLIN_SYNC_PASSWORD Sync password\n LOG_LEVEL Log level: debug, info, warn, error (default: info)\n\nMODES:\n Sidecar (default):\n Spawns and manages its own Joplin Terminal process.\n No Joplin desktop app or Web Clipper needed.\n Uses an isolated profile at --profile path (default: ~/.config/joplin-mcp).\n\n External (JOPLIN_HOST/JOPLIN_PORT set):\n Connects directly to an existing Joplin instance.\n Useful for WSL connecting to Windows Joplin desktop.\n\nEXAMPLES:\n # Minimal - local notes, no sync\n joplin-mcp-server --token my_token\n\n # Joplin Cloud sync\n joplin-mcp-server --token my_token \\\\\n --sync-target joplin-cloud \\\\\n --sync-username user@example.com --sync-password pass\n\n # WebDAV sync\n joplin-mcp-server --token my_token \\\\\n --sync-target webdav \\\\\n --sync-path https://dav.example.com/joplin \\\\\n --sync-username user --sync-password pass\n\n # Filesystem sync (Syncthing, NAS)\n joplin-mcp-server --token my_token \\\\\n --sync-target filesystem --sync-path /mnt/sync/joplin\n\n # HTTP transport for web apps\n joplin-mcp-server --token my_token --transport http --http-port 3000\n\n # External mode - connect to existing Joplin (e.g. Windows desktop from WSL)\n JOPLIN_HOST=172.x.x.x JOPLIN_PORT=41184 joplin-mcp-server --token my_token\n\nFind your Joplin token in: Tools > Options > Web Clipper\n`)\n process.exit(0)\n }\n\n return {\n remainingArgs: args,\n transport,\n httpPort,\n profileDir,\n syncTarget: resolvedSyncTarget,\n }\n}\n\nexport default parseArgs\nexport { expandPath, expandVars, resolveWslPath }\n","import { type Either } from \"functype\"\n\nimport type JoplinAPIClient from \"../joplin-api-client.js\"\n\n/**\n * Thrown by tools to signal an operation failure that should surface to the\n * caller (and the LLM agent) as an MCP error response (isError: true), rather\n * than being silently returned as a \"successful\" text result.\n */\nclass ToolError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"ToolError\"\n }\n}\n\n/**\n * Extract a Joplin-style error message from an API response body. Joplin can\n * return HTTP 200 with `{ error: \"...\" }` (or `{ error: ..., code: ... }`)\n * when an operation fails — without this check the tool would treat the\n * malformed payload as a success.\n */\nconst extractJoplinErrorMessage = (data: unknown): string | undefined => {\n if (data !== null && typeof data === \"object\" && \"error\" in data) {\n const err = (data as { error: unknown }).error\n if (typeof err === \"string\" && err.length > 0) return err\n }\n return undefined\n}\n\ntype JoplinFolder = {\n id: string\n title: string\n parent_id?: string\n}\n\ntype JoplinNote = {\n id: string\n title: string\n body?: string\n parent_id?: string\n created_time: number\n updated_time: number\n is_todo: boolean\n todo_completed?: boolean\n todo_due?: number\n}\n\nabstract class BaseTool {\n protected apiClient: JoplinAPIClient\n\n constructor(apiClient: JoplinAPIClient) {\n this.apiClient = apiClient\n }\n\n abstract call(...args: unknown[]): Promise<string>\n\n protected formatError(error: unknown, context: string): string {\n process.stderr.write(`${context} error: ${error}\\n`)\n const message = error instanceof Error ? error.message : \"Unknown error\"\n return `Error ${context.toLowerCase()}: ${message}`\n }\n\n protected validateId(id: string, type: \"note\" | \"notebook\"): string | null {\n if (!id) {\n return `Please provide a ${type} ID. Example: ${type === \"note\" ? \"read_note\" : \"read_notebook\"} ${type}_id=\"your-${type}-id\"`\n }\n\n if (id.length < 10 || !id.match(/[a-f0-9]/i)) {\n const searchHint = type === \"note\" ? \"search_notes\" : \"list_notebooks\"\n return `Error: \"${id}\" does not appear to be a valid ${type} ID. \\n\\n${type.charAt(0).toUpperCase() + type.slice(1)} IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse ${searchHint} to ${type === \"note\" ? \"find notes\" : \"see all available notebooks\"} and their IDs.`\n }\n\n return null\n }\n\n protected unwrap<T>(result: Either<Error, T>): T {\n return result.fold(\n (err) => {\n throw err\n },\n (val) => val,\n )\n }\n\n protected formatDate(timestamp: number): string {\n return new Date(timestamp).toLocaleString()\n }\n}\n\nexport default BaseTool\nexport { extractJoplinErrorMessage, ToolError }\nexport type { JoplinFolder, JoplinNote }\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface CreateFolderOptions {\n title: string\n parent_id?: string | undefined\n}\n\ninterface CreateFolderResponse extends JoplinFolder {\n created_time: number\n updated_time: number\n}\n\nclass CreateFolder extends BaseTool {\n async call(options: CreateFolderOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide folder creation options. Example: create_folder {\"title\": \"My Notebook\"}'\n }\n\n // Validate required title\n if (!options.title || typeof options.title !== \"string\" || options.title.trim() === \"\") {\n return 'Please provide a title for the folder/notebook. Example: create_folder {\"title\": \"My Notebook\"}'\n }\n\n // Validate parent_id if provided\n if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n }\n\n try {\n // Prepare the request body\n const requestBody: CreateFolderOptions = {\n title: options.title.trim(),\n }\n\n if (options.parent_id) {\n requestBody.parent_id = options.parent_id\n }\n\n // Create the folder\n const createdFolder = this.unwrap(await this.apiClient.post<CreateFolderResponse>(\"/folders\", requestBody))\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails. Catch that before treating it as success.\n const joplinError = extractJoplinErrorMessage(createdFolder)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to create notebook: ${joplinError}`)\n }\n if (!createdFolder || typeof createdFolder !== \"object\" || !createdFolder.id) {\n throw new ToolError(\n `Failed to create notebook: Joplin API returned an unexpected response (no folder id). Raw response: ${JSON.stringify(createdFolder)}`,\n )\n }\n\n // Get parent notebook info if available\n const parentInfo = await (async (): Promise<string> => {\n if (!createdFolder.parent_id) return \"Top level\"\n try {\n const parentNotebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${createdFolder.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (parentNotebook?.title) {\n return `Inside \"${parentNotebook.title}\" (notebook_id: \"${createdFolder.parent_id}\")`\n }\n return `Parent notebook ID: ${createdFolder.parent_id}`\n } catch {\n return `Parent notebook ID: ${createdFolder.parent_id}`\n }\n })()\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully created notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Notebook Details:`)\n resultLines.push(` Title: \"${createdFolder.title}\"`)\n resultLines.push(` Notebook ID: ${createdFolder.id}`)\n resultLines.push(` Location: ${parentInfo}`)\n\n const createdDate = this.formatDate(createdFolder.created_time)\n resultLines.push(` Created: ${createdDate}`)\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${createdFolder.id}\"`)\n resultLines.push(` - Create a note in it: create_note {\"title\": \"My Note\", \"parent_id\": \"${createdFolder.id}\"}`)\n resultLines.push(` - View all notebooks: list_notebooks`)\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number; data?: { error?: string } } }\n process.stderr.write(`creating notebook error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to create notebook: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n if (err.response.status === 404 && options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to create notebook: Parent notebook with ID \"${options.parent_id}\" not found. Use list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`,\n )\n }\n if (err.response.status === 409) {\n throw new ToolError(\n `Failed to create notebook: A notebook with the title \"${options.title}\" might already exist in this location. Try a different title or check existing notebooks with list_notebooks.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to create notebook: ${message}`)\n }\n }\n}\n\nexport default CreateFolder\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface CreateNoteOptions {\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n image_data_url?: string | undefined\n}\n\ntype CreateNoteResponse = JoplinNote\n\nclass CreateNote extends BaseTool {\n async call(options: CreateNoteOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide note creation options. Example: create_note {\"title\": \"My Note\", \"body\": \"Note content\"}'\n }\n\n // Validate that we have at least a title or body\n if (!options.title && !options.body && !options.body_html) {\n return \"Please provide at least a title, body, or body_html for the note.\"\n }\n\n // Validate parent_id if provided\n if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n }\n\n try {\n // Prepare the request body\n const requestBody: CreateNoteOptions = {}\n\n if (options.title) requestBody.title = options.title\n if (options.body) requestBody.body = options.body\n if (options.body_html) requestBody.body_html = options.body_html\n if (options.parent_id) requestBody.parent_id = options.parent_id\n if (options.is_todo !== undefined) requestBody.is_todo = options.is_todo\n if (options.image_data_url) requestBody.image_data_url = options.image_data_url\n\n // Create the note\n const createdNote = this.unwrap(await this.apiClient.post<CreateNoteResponse>(\"/notes\", requestBody))\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails (e.g. invalid parent_id). Catch that before treating\n // it as a successful response.\n const joplinError = extractJoplinErrorMessage(createdNote)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to create note: ${joplinError}`)\n }\n if (!createdNote || typeof createdNote !== \"object\" || !createdNote.id) {\n throw new ToolError(\n `Failed to create note: Joplin API returned an unexpected response (no note id). Raw response: ${JSON.stringify(createdNote)}`,\n )\n }\n\n // Get notebook info if available\n const notebookInfo = await (async (): Promise<string> => {\n if (!createdNote.parent_id) return \"Root level\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${createdNote.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${createdNote.parent_id}\")`\n }\n return `Notebook ID: ${createdNote.parent_id}`\n } catch {\n return `Notebook ID: ${createdNote.parent_id}`\n }\n })()\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully created note!`)\n resultLines.push(\"\")\n resultLines.push(`📝 Note Details:`)\n resultLines.push(` Title: \"${createdNote.title || \"Untitled\"}\"`)\n resultLines.push(` Note ID: ${createdNote.id}`)\n resultLines.push(` Location: ${notebookInfo}`)\n\n if (createdNote.is_todo) {\n resultLines.push(` Type: Todo item`)\n }\n\n const createdDate = this.formatDate(createdNote.created_time)\n resultLines.push(` Created: ${createdDate}`)\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - Read the note: read_note note_id=\"${createdNote.id}\"`)\n if (createdNote.parent_id) {\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${createdNote.parent_id}\"`)\n }\n resultLines.push(` - Search for it: search_notes query=\"${createdNote.title}\"`)\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n // Re-throw ToolErrors as-is (already formatted for the caller)\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number; data?: { error?: string } } }\n process.stderr.write(`creating note error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to create note: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n if (err.response.status === 404 && options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to create note: Notebook with ID \"${options.parent_id}\" not found. Use list_notebooks to see available notebooks and their IDs.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to create note: ${message}`)\n }\n }\n}\n\nexport default CreateNote\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface DeleteFolderOptions {\n folder_id: string\n confirm?: boolean | undefined\n force?: boolean | undefined\n}\n\ntype FolderItem = { id: string; title?: string; parent_id?: string }\n\ninterface FolderContents {\n items: FolderItem[]\n}\n\nclass DeleteFolder extends BaseTool {\n async call(options: DeleteFolderOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n }\n\n // Validate required folder_id\n if (!options.folder_id) {\n return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n }\n\n const folderIdError = this.validateId(options.folder_id, \"notebook\")\n if (folderIdError) {\n return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n }\n\n // Require explicit confirmation for safety\n if (!options.confirm) {\n return `⚠️ This will permanently delete the notebook/folder!\\n\\nTo confirm deletion, use:\\ndelete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true}\\n\\n⚠️ This action cannot be undone!`\n }\n\n try {\n // First, get the folder details to show what's being deleted\n const folderToDelete = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const folderLoadError = extractJoplinErrorMessage(folderToDelete)\n if (folderLoadError !== undefined) {\n throw new ToolError(`Failed to load folder \"${options.folder_id}\": ${folderLoadError}`)\n }\n if (!folderToDelete?.id) {\n throw new ToolError(\n `Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n\n // Check if folder contains notes or subfolders\n const [notes, subfolders] = await Promise.all([\n this.apiClient\n .get<FolderContents>(`/folders/${options.folder_id}/notes`, {\n query: { fields: \"id,title\" },\n })\n .then((result) =>\n result.fold(\n () => ({ items: [] }),\n (data) => data,\n ),\n ),\n this.apiClient\n .get<FolderContents>(\"/folders\", {\n query: { fields: \"id,title,parent_id\" },\n })\n .then((result) =>\n result.fold<FolderContents>(\n () => ({ items: [] }),\n (response) => ({\n items: response.items.filter((folder) => folder.parent_id === options.folder_id),\n }),\n ),\n ),\n ])\n\n const noteCount = notes.items.length\n const subfolderCount = subfolders.items.length\n const totalContent = noteCount + subfolderCount\n\n // Warn if folder is not empty and force is not specified\n if (totalContent > 0 && !options.force) {\n const resultLines: string[] = []\n resultLines.push(`⚠️ Cannot delete non-empty notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Notebook: \"${folderToDelete.title}\"`)\n resultLines.push(` Contains: ${noteCount} notes and ${subfolderCount} subfolders`)\n\n if (noteCount > 0) {\n resultLines.push(\"\")\n resultLines.push(`📝 Contains ${noteCount} notes:`)\n notes.items.slice(0, 5).forEach((note) => {\n resultLines.push(` - ${note.title ?? \"Untitled\"}`)\n })\n if (noteCount > 5) {\n resultLines.push(` ... and ${noteCount - 5} more notes`)\n }\n }\n\n if (subfolderCount > 0) {\n resultLines.push(\"\")\n resultLines.push(`📁 Contains ${subfolderCount} subfolders:`)\n subfolders.items.slice(0, 5).forEach((folder) => {\n resultLines.push(` - ${folder.title ?? \"Untitled\"}`)\n })\n if (subfolderCount > 5) {\n resultLines.push(` ... and ${subfolderCount - 5} more folders`)\n }\n }\n\n resultLines.push(\"\")\n resultLines.push(`💡 Options:`)\n resultLines.push(` 1. Move or delete the contents first, then delete the folder`)\n resultLines.push(` 2. Force delete (⚠️ DESTROYS ALL CONTENT):`)\n resultLines.push(` delete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true, \"force\": true}`)\n resultLines.push(\"\")\n resultLines.push(`⚠️ Force delete will permanently delete ALL ${totalContent} items inside!`)\n\n return resultLines.join(\"\\n\")\n }\n\n // Get parent folder info if available\n const parentInfo = await (async (): Promise<string> => {\n if (!folderToDelete.parent_id) return \"Top level\"\n try {\n const parentFolder = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${folderToDelete.parent_id}`, {\n query: { fields: \"title\" },\n }),\n )\n if (parentFolder?.title) {\n return `Inside \"${parentFolder.title}\" (notebook_id: \"${folderToDelete.parent_id}\")`\n }\n return `Parent ID: ${folderToDelete.parent_id}`\n } catch {\n return `Parent ID: ${folderToDelete.parent_id}`\n }\n })()\n\n // Delete the folder\n const deleteResponse = this.unwrap(await this.apiClient.delete(`/folders/${options.folder_id}`))\n const deleteError = extractJoplinErrorMessage(deleteResponse)\n if (deleteError !== undefined) {\n throw new ToolError(`Failed to delete folder: ${deleteError}`)\n }\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`🗑️ Successfully deleted notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Deleted Notebook Details:`)\n resultLines.push(` Title: \"${folderToDelete.title}\"`)\n resultLines.push(` Folder ID: ${folderToDelete.id}`)\n resultLines.push(` Location: ${parentInfo}`)\n\n if (totalContent > 0) {\n resultLines.push(` Deleted Content: ${noteCount} notes and ${subfolderCount} subfolders`)\n resultLines.push(\"\")\n resultLines.push(`⚠️ All ${totalContent} items inside have been permanently deleted!`)\n }\n\n resultLines.push(\"\")\n resultLines.push(`⚠️ This notebook has been permanently deleted and cannot be recovered.`)\n\n if (folderToDelete.parent_id) {\n resultLines.push(\"\")\n resultLines.push(`🔗 Related actions:`)\n resultLines.push(` - View parent notebook: read_notebook notebook_id=\"${folderToDelete.parent_id}\"`)\n resultLines.push(` - View all notebooks: list_notebooks`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`deleting folder error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404) {\n throw new ToolError(\n `Failed to delete folder: Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n if (err.response.status === 403) {\n throw new ToolError(\n `Failed to delete folder: Permission denied for folder with ID \"${options.folder_id}\". This might be a protected system folder.`,\n )\n }\n if (err.response.status === 409) {\n throw new ToolError(\n `Failed to delete folder: It may contain items that prevent deletion. Try moving or deleting the contents first, or use force option.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to delete folder: ${message}`)\n }\n }\n}\n\nexport default DeleteFolder\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface DeleteNoteOptions {\n note_id: string\n confirm?: boolean | undefined\n}\n\nclass DeleteNote extends BaseTool {\n async call(options: DeleteNoteOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n }\n\n // Validate required note_id\n if (!options.note_id) {\n return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n }\n\n const noteIdError = this.validateId(options.note_id, \"note\")\n if (noteIdError) {\n return noteIdError\n }\n\n // Require explicit confirmation for safety\n if (!options.confirm) {\n return `⚠️ This will permanently delete the note!\\n\\nTo confirm deletion, use:\\ndelete_note {\"note_id\": \"${options.note_id}\", \"confirm\": true}\\n\\n⚠️ This action cannot be undone!`\n }\n\n try {\n // First, get the note details to show what's being deleted\n const noteToDelete = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,created_time,updated_time\" },\n }),\n )\n\n const noteLoadError = extractJoplinErrorMessage(noteToDelete)\n if (noteLoadError !== undefined) {\n throw new ToolError(`Failed to load note \"${options.note_id}\": ${noteLoadError}`)\n }\n if (!noteToDelete?.id) {\n throw new ToolError(\n `Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n\n // Get notebook info if available\n const notebookInfo = await (async (): Promise<string> => {\n if (!noteToDelete.parent_id) return \"Root level\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${noteToDelete.parent_id}`, {\n query: { fields: \"title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${noteToDelete.parent_id}\")`\n }\n return `Notebook ID: ${noteToDelete.parent_id}`\n } catch {\n return `Notebook ID: ${noteToDelete.parent_id}`\n }\n })()\n\n // Delete the note\n const deleteResponse = this.unwrap(await this.apiClient.delete(`/notes/${options.note_id}`))\n const deleteError = extractJoplinErrorMessage(deleteResponse)\n if (deleteError !== undefined) {\n throw new ToolError(`Failed to delete note: ${deleteError}`)\n }\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`🗑️ Successfully deleted note!`)\n resultLines.push(\"\")\n resultLines.push(`📝 Deleted Note Details:`)\n resultLines.push(` Title: \"${noteToDelete.title || \"Untitled\"}\"`)\n resultLines.push(` Note ID: ${noteToDelete.id}`)\n resultLines.push(` Location: ${notebookInfo}`)\n\n if (noteToDelete.is_todo) {\n const status = noteToDelete.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(` Type: Todo (${status})`)\n } else {\n resultLines.push(` Type: Regular note`)\n }\n\n const createdDate = this.formatDate(noteToDelete.created_time)\n const updatedDate = this.formatDate(noteToDelete.updated_time)\n resultLines.push(` Created: ${createdDate}`)\n resultLines.push(` Last Updated: ${updatedDate}`)\n\n // Show content preview if available\n if (noteToDelete.body) {\n const preview = noteToDelete.body.substring(0, 100).replace(/\\n/g, \" \")\n const truncated = noteToDelete.body.length > 100 ? \"...\" : \"\"\n resultLines.push(` Content Preview: ${preview}${truncated}`)\n }\n\n resultLines.push(\"\")\n resultLines.push(`⚠️ This note has been permanently deleted and cannot be recovered.`)\n\n if (noteToDelete.parent_id) {\n resultLines.push(\"\")\n resultLines.push(`🔗 Related actions:`)\n resultLines.push(` - View containing notebook: read_notebook notebook_id=\"${noteToDelete.parent_id}\"`)\n resultLines.push(` - Search for similar notes: search_notes query=\"${noteToDelete.title}\"`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`deleting note error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404) {\n throw new ToolError(\n `Failed to delete note: Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n if (err.response.status === 403) {\n throw new ToolError(\n `Failed to delete note: Permission denied for note with ID \"${options.note_id}\". This might be a protected system note.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to delete note: ${message}`)\n }\n }\n}\n\nexport default DeleteNote\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface EditFolderOptions {\n folder_id: string\n title?: string | undefined\n parent_id?: string | undefined\n}\n\ninterface EditFolderResponse extends JoplinFolder {\n updated_time: number\n}\n\nclass EditFolder extends BaseTool {\n async call(options: EditFolderOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n }\n\n // Validate required folder_id\n if (!options.folder_id) {\n return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n }\n\n const folderIdError = this.validateId(options.folder_id, \"notebook\")\n if (folderIdError) {\n return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n }\n\n // Validate that we have at least one field to update\n const updateFields = [\"title\", \"parent_id\"]\n const hasUpdate = updateFields.some((field) => options[field as keyof EditFolderOptions] !== undefined)\n\n if (!hasUpdate) {\n return \"Please provide at least one field to update. Available fields: title, parent_id\"\n }\n\n // Validate title if provided\n if (options.title !== undefined && (typeof options.title !== \"string\" || options.title.trim() === \"\")) {\n return \"Title must be a non-empty string.\"\n }\n\n // Validate parent_id if provided\n if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n }\n\n // Prevent self-parenting\n if (options.parent_id === options.folder_id) {\n return \"Error: A folder cannot be its own parent.\"\n }\n }\n\n try {\n // First, get the current folder to show before/after comparison\n const currentFolder = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const currentFolderError = extractJoplinErrorMessage(currentFolder)\n if (currentFolderError !== undefined) {\n throw new ToolError(`Failed to load folder \"${options.folder_id}\": ${currentFolderError}`)\n }\n if (!currentFolder?.id) {\n throw new ToolError(\n `Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n\n // Prepare the update body - only include fields that are being updated\n const updateBody: Partial<EditFolderOptions> = {}\n\n if (options.title !== undefined) updateBody.title = options.title.trim()\n if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n\n // Update the folder\n const updatedFolder = this.unwrap(\n await this.apiClient.put<EditFolderResponse>(`/folders/${options.folder_id}`, updateBody),\n )\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails. Catch that before treating it as success.\n const joplinError = extractJoplinErrorMessage(updatedFolder)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to update folder: ${joplinError}`)\n }\n if (!updatedFolder || typeof updatedFolder !== \"object\" || !updatedFolder.id) {\n throw new ToolError(\n `Failed to update folder: Joplin API returned an unexpected response (no folder id). Raw response: ${JSON.stringify(updatedFolder)}`,\n )\n }\n\n // Get parent folder info for both old and new locations if parent_id changed\n const fetchParentInfo = async (parentId: string): Promise<string> => {\n try {\n const parent = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${parentId}`, {\n query: { fields: \"title\" },\n }),\n )\n if (parent?.title) {\n return `Inside \"${parent.title}\"`\n }\n return `Parent ID: ${parentId}`\n } catch {\n return `Parent ID: ${parentId}`\n }\n }\n\n const oldParentInfo = currentFolder.parent_id ? await fetchParentInfo(currentFolder.parent_id) : \"Top level\"\n const newParentInfo = await (async (): Promise<string> => {\n if (!updatedFolder.parent_id) return \"Top level\"\n if (updatedFolder.parent_id === currentFolder.parent_id) return oldParentInfo\n return fetchParentInfo(updatedFolder.parent_id)\n })()\n\n // Format success response with before/after comparison\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully updated notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Notebook: \"${updatedFolder.title}\"`)\n resultLines.push(` Folder ID: ${updatedFolder.id}`)\n resultLines.push(\"\")\n\n // Show what changed\n resultLines.push(`🔄 Changes made:`)\n\n if (options.title !== undefined && currentFolder.title !== updatedFolder.title) {\n resultLines.push(` Title: \"${currentFolder.title}\" → \"${updatedFolder.title}\"`)\n }\n\n if (options.parent_id !== undefined && currentFolder.parent_id !== updatedFolder.parent_id) {\n resultLines.push(` Location: ${oldParentInfo} → ${newParentInfo}`)\n }\n\n if (updatedFolder.updated_time) {\n const updatedTime = this.formatDate(updatedFolder.updated_time)\n resultLines.push(` Last Updated: ${updatedTime}`)\n }\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${updatedFolder.id}\"`)\n resultLines.push(` - View all notebooks: list_notebooks`)\n if (updatedFolder.parent_id) {\n resultLines.push(` - View parent notebook: read_notebook notebook_id=\"${updatedFolder.parent_id}\"`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as {\n response?: { status?: number; data?: { error?: string } }\n config?: { url?: string }\n }\n process.stderr.write(`updating folder error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404) {\n if (err.config?.url?.includes(`/folders/${options.folder_id}`) === true) {\n throw new ToolError(\n `Failed to update folder: Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n if (options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to update folder: Parent folder with ID \"${options.parent_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n }\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to update folder: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n if (err.response.status === 409) {\n throw new ToolError(\n `Failed to update folder: A folder with the title \"${options.title ?? \"\"}\" might already exist in this location. Try a different title or check existing folders with list_notebooks.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to update folder: ${message}`)\n }\n }\n}\n\nexport default EditFolder\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface EditNoteOptions {\n note_id: string\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n todo_completed?: boolean | undefined\n todo_due?: number | undefined\n}\n\ntype EditNoteResponse = JoplinNote\n\nclass EditNote extends BaseTool {\n async call(options: EditNoteOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n }\n\n // Validate required note_id\n if (!options.note_id) {\n return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n }\n\n const noteIdError = this.validateId(options.note_id, \"note\")\n if (noteIdError) {\n return noteIdError\n }\n\n // Validate that we have at least one field to update\n const updateFields = [\"title\", \"body\", \"body_html\", \"parent_id\", \"is_todo\", \"todo_completed\", \"todo_due\"]\n const hasUpdate = updateFields.some((field) => options[field as keyof EditNoteOptions] !== undefined)\n\n if (!hasUpdate) {\n return \"Please provide at least one field to update. Available fields: title, body, body_html, parent_id, is_todo, todo_completed, todo_due\"\n }\n\n // Validate parent_id if provided\n if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n }\n }\n\n try {\n // First, get the current note to show before/after comparison\n const currentNote = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,todo_due,updated_time\" },\n }),\n )\n\n const currentNoteError = extractJoplinErrorMessage(currentNote)\n if (currentNoteError !== undefined) {\n throw new ToolError(`Failed to load note \"${options.note_id}\": ${currentNoteError}`)\n }\n if (!currentNote?.id) {\n throw new ToolError(\n `Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n\n // Prepare the update body - only include fields that are being updated\n const updateBody: Partial<EditNoteOptions> = {}\n\n if (options.title !== undefined) updateBody.title = options.title\n if (options.body !== undefined) updateBody.body = options.body\n if (options.body_html !== undefined) updateBody.body_html = options.body_html\n if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n if (options.is_todo !== undefined) updateBody.is_todo = options.is_todo\n if (options.todo_completed !== undefined) updateBody.todo_completed = options.todo_completed\n if (options.todo_due !== undefined) updateBody.todo_due = options.todo_due\n\n // Update the note\n const updatedNote = this.unwrap(\n await this.apiClient.put<EditNoteResponse>(`/notes/${options.note_id}`, updateBody),\n )\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails. Catch that before treating it as success.\n const joplinError = extractJoplinErrorMessage(updatedNote)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to update note: ${joplinError}`)\n }\n if (!updatedNote || typeof updatedNote !== \"object\" || !updatedNote.id) {\n throw new ToolError(\n `Failed to update note: Joplin API returned an unexpected response (no note id). Raw response: ${JSON.stringify(updatedNote)}`,\n )\n }\n\n // Get notebook info for both old and new locations if parent_id changed\n const fetchNotebookInfo = async (parentId: string): Promise<string> => {\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${parentId}`, {\n query: { fields: \"title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\"`\n }\n return `Notebook ID: ${parentId}`\n } catch {\n return `Notebook ID: ${parentId}`\n }\n }\n\n const oldNotebookInfo = currentNote.parent_id ? await fetchNotebookInfo(currentNote.parent_id) : \"Root level\"\n const newNotebookInfo = await (async (): Promise<string> => {\n if (!updatedNote.parent_id) return \"Root level\"\n if (updatedNote.parent_id === currentNote.parent_id) return oldNotebookInfo\n return fetchNotebookInfo(updatedNote.parent_id)\n })()\n\n // Format success response with before/after comparison\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully updated note!`)\n resultLines.push(\"\")\n resultLines.push(`📝 Note: \"${updatedNote.title || \"Untitled\"}\"`)\n resultLines.push(` Note ID: ${updatedNote.id}`)\n resultLines.push(\"\")\n\n // Show what changed\n resultLines.push(`🔄 Changes made:`)\n\n if (options.title !== undefined && currentNote.title !== updatedNote.title) {\n resultLines.push(` Title: \"${currentNote.title}\" → \"${updatedNote.title}\"`)\n }\n\n if (options.parent_id !== undefined && currentNote.parent_id !== updatedNote.parent_id) {\n resultLines.push(` Location: ${oldNotebookInfo} → ${newNotebookInfo}`)\n }\n\n if (options.is_todo !== undefined && currentNote.is_todo !== updatedNote.is_todo) {\n const oldType = currentNote.is_todo ? \"Todo\" : \"Regular note\"\n const newType = updatedNote.is_todo ? \"Todo\" : \"Regular note\"\n resultLines.push(` Type: ${oldType} → ${newType}`)\n }\n\n if (options.todo_completed !== undefined && currentNote.todo_completed !== updatedNote.todo_completed) {\n const oldStatus = currentNote.todo_completed ? \"Completed\" : \"Not completed\"\n const newStatus = updatedNote.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(` Todo Status: ${oldStatus} → ${newStatus}`)\n }\n\n if (options.todo_due !== undefined) {\n const oldDue = currentNote.todo_due ? this.formatDate(currentNote.todo_due) : \"No due date\"\n const newDue = updatedNote.todo_due ? this.formatDate(updatedNote.todo_due) : \"No due date\"\n if (oldDue !== newDue) {\n resultLines.push(` Due Date: ${oldDue} → ${newDue}`)\n }\n }\n\n if (options.body !== undefined) {\n resultLines.push(` Content: Updated`)\n }\n\n if (options.body_html !== undefined) {\n resultLines.push(` HTML Content: Updated`)\n }\n\n const updatedTime = this.formatDate(updatedNote.updated_time)\n resultLines.push(` Last Updated: ${updatedTime}`)\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - Read the note: read_note note_id=\"${updatedNote.id}\"`)\n if (updatedNote.parent_id) {\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${updatedNote.parent_id}\"`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number; data?: { error?: string } } }\n process.stderr.write(`updating note error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404 && options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to update note: Notebook with ID \"${options.parent_id}\" not found. Use list_notebooks to see available notebooks and their IDs.`,\n )\n }\n if (err.response.status === 404) {\n throw new ToolError(\n `Failed to update note: Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to update note: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to update note: ${message}`)\n }\n }\n}\n\nexport default EditNote\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { ToolError } from \"./base-tool.js\"\n\nclass ListNotebooks extends BaseTool {\n async call(): Promise<string> {\n try {\n const notebooks = this.unwrap(\n await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const notebooksByParentId: Record<string, JoplinFolder[]> = {}\n\n notebooks.forEach((notebook) => {\n const parentId = notebook.parent_id ?? \"\"\n if (!notebooksByParentId[parentId]) {\n notebooksByParentId[parentId] = []\n }\n notebooksByParentId[parentId].push(notebook)\n })\n\n // Add a header with instructions\n const resultLines = [\n \"Joplin Notebooks:\\n\",\n \"NOTE: To read a notebook, use the notebook_id with the read_notebook command\\n\",\n 'Example: read_notebook notebook_id=\"your-notebook-id\"\\n\\n',\n ]\n\n // Add the notebook hierarchy\n resultLines.push(\n ...this.notebooksLines(notebooksByParentId[\"\"] ?? [], {\n indent: 0,\n notebooksByParentId,\n }),\n )\n\n return resultLines.join(\"\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n process.stderr.write(`listing notebooks error: ${error}\\n`)\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to list notebooks: ${message}`)\n }\n }\n\n private notebooksLines(\n notebooks: JoplinFolder[],\n {\n indent = 0,\n notebooksByParentId,\n }: {\n indent: number\n notebooksByParentId: Record<string, JoplinFolder[]>\n },\n ): string[] {\n const result: string[] = []\n const indentSpaces = \" \".repeat(indent)\n\n this.sortNotebooks(notebooks).forEach((notebook) => {\n const { id } = notebook\n result.push(`${indentSpaces}Notebook: \"${notebook.title}\" (notebook_id: \"${id}\")\\n`)\n\n const childNotebooks = notebooksByParentId[id]\n if (childNotebooks) {\n result.push(\n ...this.notebooksLines(childNotebooks, {\n indent: indent + 2,\n notebooksByParentId,\n }),\n )\n }\n })\n\n return result\n }\n\n private sortNotebooks(notebooks: JoplinFolder[]): JoplinFolder[] {\n // Ensure that notebooks starting with '[0]' are sorted first\n const CHARACTER_BEFORE_A = String.fromCharCode(\"A\".charCodeAt(0) - 1)\n return [...notebooks].sort((a, b) => {\n const titleA = a.title.replace(\"[\", CHARACTER_BEFORE_A)\n const titleB = b.title.replace(\"[\", CHARACTER_BEFORE_A)\n return titleA.localeCompare(titleB)\n })\n }\n}\n\nexport default ListNotebooks\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool from \"./base-tool.js\"\n\nclass ReadMultiNote extends BaseTool {\n async call(noteIds: string[]): Promise<string> {\n if (!noteIds || !Array.isArray(noteIds) || noteIds.length === 0) {\n return 'Please provide an array of note IDs. Example: read_multinote note_ids=[\"id1\", \"id2\", \"id3\"]'\n }\n\n // Validate that all IDs look like valid note IDs\n const invalidIds = noteIds.filter((id) => !id || id.length < 10 || !id.match(/[a-f0-9]/i))\n if (invalidIds.length > 0) {\n return `Error: Some IDs do not appear to be valid note IDs: ${invalidIds.join(\", \")}\\n\\nNote IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse search_notes to find notes and their IDs.`\n }\n\n const resultLines: string[] = []\n const notFound: string[] = []\n const errors: string[] = []\n const successful: string[] = []\n\n // Add a header\n resultLines.push(`# Reading ${noteIds.length} notes\\n`)\n\n // Process each note ID\n for (const [i, noteId] of noteIds.entries()) {\n resultLines.push(`## Note ${i + 1} of ${noteIds.length} (ID: ${noteId})\\n`)\n\n try {\n // Get the note details with all relevant fields\n const note = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n query: {\n fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n },\n }),\n )\n\n // Validate note response\n if (!note || typeof note !== \"object\" || !note.id) {\n errors.push(noteId)\n resultLines.push(`Error: Unexpected response format from Joplin API when fetching note ${noteId}\\n`)\n continue\n }\n\n successful.push(noteId)\n\n // Get the notebook info to show where this note is located\n const notebookInfo = await (async (): Promise<string> => {\n if (!note.parent_id) return \"Unknown notebook\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n }\n return \"Unknown notebook\"\n } catch (err: unknown) {\n process.stderr.write(`Error fetching notebook info for note ${noteId}: ${err}\\n`)\n return \"Unknown notebook\"\n }\n })()\n\n // Add note metadata\n resultLines.push(`### Note: \"${note.title}\"`)\n resultLines.push(`Notebook: ${notebookInfo}`)\n\n // Add todo status if applicable\n if (note.is_todo) {\n const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(`Status: ${status}`)\n\n if (note.todo_due) {\n const dueDate = this.formatDate(note.todo_due)\n resultLines.push(`Due: ${dueDate}`)\n }\n }\n\n // Add timestamps\n const createdDate = this.formatDate(note.created_time)\n const updatedDate = this.formatDate(note.updated_time)\n resultLines.push(`Created: ${createdDate}`)\n resultLines.push(`Updated: ${updatedDate}`)\n\n // Add a separator before the note content\n resultLines.push(\"\\n---\\n\")\n\n // Add the note body\n if (note.body) {\n resultLines.push(note.body)\n } else {\n resultLines.push(\"(This note has no content)\")\n }\n\n // Add a separator after the note\n resultLines.push(\"\\n---\\n\")\n } catch (error: unknown) {\n process.stderr.write(`Error reading note ${noteId}: ${error}\\n`)\n const err = error as { response?: { status?: number }; message?: string }\n if (err.response?.status === 404) {\n notFound.push(noteId)\n resultLines.push(`Note with ID \"${noteId}\" not found.\\n`)\n } else {\n errors.push(noteId)\n resultLines.push(`Error reading note: ${err.message ?? \"Unknown error\"}\\n`)\n }\n }\n }\n\n // Add a summary at the end\n resultLines.push(\"# Summary\")\n resultLines.push(`Total notes requested: ${noteIds.length}`)\n resultLines.push(`Successfully retrieved: ${successful.length}`)\n\n if (notFound.length > 0) {\n resultLines.push(`Notes not found: ${notFound.length}`)\n resultLines.push(`IDs not found: ${notFound.join(\", \")}`)\n }\n\n if (errors.length > 0) {\n resultLines.push(`Errors encountered: ${errors.length}`)\n resultLines.push(`IDs with errors: ${errors.join(\", \")}`)\n }\n\n return resultLines.join(\"\\n\")\n }\n}\n\nexport default ReadMultiNote\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\nclass ReadNote extends BaseTool {\n async call(noteId: string): Promise<string> {\n const validationError = this.validateId(noteId, \"note\")\n if (validationError) {\n return validationError\n }\n\n try {\n // Get the note details with all relevant fields\n const note = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n query: {\n fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n },\n }),\n )\n\n const noteLoadError = extractJoplinErrorMessage(note)\n if (noteLoadError !== undefined) {\n throw new ToolError(`Failed to read note \"${noteId}\": ${noteLoadError}`)\n }\n if (!note || typeof note !== \"object\" || !note.id) {\n throw new ToolError(\n `Failed to read note \"${noteId}\": Joplin API returned an unexpected response (no note id). Raw response: ${JSON.stringify(note)}`,\n )\n }\n\n // Get the notebook info to show where this note is located\n const notebookInfo = await (async (): Promise<string> => {\n if (!note.parent_id) return \"Unknown notebook\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n }\n return \"Unknown notebook\"\n } catch (err: unknown) {\n process.stderr.write(`Error fetching notebook info: ${err}\\n`)\n return \"Unknown notebook\"\n }\n })()\n\n // Format the note content\n const resultLines: string[] = []\n\n // Add note header with metadata\n resultLines.push(`# Note: \"${note.title}\"`)\n resultLines.push(`Note ID: ${note.id}`)\n resultLines.push(`Notebook: ${notebookInfo}`)\n\n // Add todo status if applicable\n if (note.is_todo) {\n const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(`Status: ${status}`)\n\n if (note.todo_due) {\n const dueDate = this.formatDate(note.todo_due)\n resultLines.push(`Due: ${dueDate}`)\n }\n }\n\n // Add timestamps\n const createdDate = this.formatDate(note.created_time)\n const updatedDate = this.formatDate(note.updated_time)\n resultLines.push(`Created: ${createdDate}`)\n resultLines.push(`Updated: ${updatedDate}`)\n\n // Add a separator before the note content\n resultLines.push(\"\\n---\\n\")\n\n // Add the note body\n if (note.body) {\n resultLines.push(note.body)\n } else {\n resultLines.push(\"(This note has no content)\")\n }\n\n // Add a footer with helpful commands\n resultLines.push(\"\\n---\\n\")\n resultLines.push(\"Related commands:\")\n resultLines.push(`- To view the notebook containing this note: read_notebook notebook_id=\"${note.parent_id}\"`)\n resultLines.push('- To search for more notes: search_notes query=\"your search term\"')\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`reading note error: ${error}\\n`)\n if (err.response?.status === 404) {\n throw new ToolError(\n `Note with ID \"${noteId}\" not found. This might happen if the ID is incorrect, you're using a notebook ID instead of a note ID, or the note has been deleted. Use search_notes to find notes and their IDs.`,\n )\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(\n `Failed to read note \"${noteId}\": ${message}. Make sure you're using a valid note ID. Use search_notes to find notes and their IDs.`,\n )\n }\n }\n}\n\nexport default ReadNote\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface NotebookNotesResponse {\n items: JoplinNote[]\n}\n\nclass ReadNotebook extends BaseTool {\n async call(notebookId: string): Promise<string> {\n const validationError = this.validateId(notebookId, \"notebook\")\n if (validationError) {\n return validationError\n }\n\n try {\n // First, get the notebook details\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${notebookId}`, {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const notebookLoadError = extractJoplinErrorMessage(notebook)\n if (notebookLoadError !== undefined) {\n throw new ToolError(`Failed to read notebook \"${notebookId}\": ${notebookLoadError}`)\n }\n if (!notebook || typeof notebook !== \"object\" || !notebook.id) {\n throw new ToolError(\n `Failed to read notebook \"${notebookId}\": Joplin API returned an unexpected response (no notebook id). Raw response: ${JSON.stringify(notebook)}`,\n )\n }\n\n // Get all notes in this notebook\n const notes = this.unwrap(\n await this.apiClient.get<NotebookNotesResponse>(`/folders/${notebookId}/notes`, {\n query: { fields: \"id,title,updated_time,is_todo,todo_completed\" },\n }),\n )\n\n const notesLoadError = extractJoplinErrorMessage(notes)\n if (notesLoadError !== undefined) {\n throw new ToolError(`Failed to load notes for notebook \"${notebookId}\": ${notesLoadError}`)\n }\n if (!notes || typeof notes !== \"object\") {\n throw new ToolError(\n `Failed to load notes for notebook \"${notebookId}\": Joplin API returned an unexpected response. Raw response: ${JSON.stringify(notes)}`,\n )\n }\n\n if (!notes.items || !Array.isArray(notes.items) || notes.items.length === 0) {\n return `Notebook \"${notebook.title}\" (notebook_id: \"${notebook.id}\") is empty.\\n\\nTry another notebook ID or use list_notebooks to see all available notebooks.`\n }\n\n // Format the notebook contents\n const resultLines: string[] = []\n resultLines.push(`# Notebook: \"${notebook.title}\" (notebook_id: \"${notebook.id}\")`)\n resultLines.push(`Contains ${notes.items.length} notes:\\n`)\n resultLines.push(`NOTE: This is showing the contents of notebook \"${notebook.title}\", not a specific note.\\n`)\n\n // If multiple notes were found, add a hint about read_multinote\n if (notes.items.length > 1) {\n const noteIds = notes.items.map((note) => note.id)\n resultLines.push(`TIP: To read all ${notes.items.length} notes at once, use:\\n`)\n resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n }\n\n // Sort notes by updated_time (newest first)\n const sortedNotes = [...notes.items].sort((a, b) => b.updated_time - a.updated_time)\n\n sortedNotes.forEach((note) => {\n const updatedDate = this.formatDate(note.updated_time)\n\n // Add checkbox for todos\n if (note.is_todo) {\n const checkboxStatus = note.todo_completed ? \"✅\" : \"☐\"\n resultLines.push(`- ${checkboxStatus} Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n } else {\n resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n }\n\n resultLines.push(` Updated: ${updatedDate}`)\n resultLines.push(` To read this note: read_note note_id=\"${note.id}\"`)\n resultLines.push(\"\") // Empty line between notes\n })\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`reading notebook error: ${error}\\n`)\n if (err.response?.status === 404) {\n throw new ToolError(\n `Notebook with ID \"${notebookId}\" not found. This might happen if the ID is incorrect, you're using a note title instead of a notebook ID, or the notebook has been deleted. Use list_notebooks to see all available notebooks with their IDs.`,\n )\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(\n `Failed to read notebook \"${notebookId}\": ${message}. Make sure you're using a valid notebook ID, not a note title. Use list_notebooks to see all available notebooks with their IDs.`,\n )\n }\n }\n}\n\nexport default ReadNotebook\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { ToolError } from \"./base-tool.js\"\n\ninterface SearchResult {\n items: JoplinNote[]\n}\n\nclass SearchNotes extends BaseTool {\n async call(query: string): Promise<string> {\n if (!query) {\n return \"Please provide a search query.\"\n }\n\n try {\n // Search for notes with the given query\n const searchResults = this.unwrap(\n await this.apiClient.get<SearchResult>(\"/search\", {\n query: {\n query,\n fields: \"id,title,body,parent_id,updated_time\",\n },\n }),\n )\n\n // Handle case where the API doesn't return the expected structure\n if (!searchResults || typeof searchResults !== \"object\") {\n return `Error: Unexpected response format from Joplin API`\n }\n\n // Handle case where no items were found\n if (!searchResults.items || !Array.isArray(searchResults.items) || searchResults.items.length === 0) {\n return `No notes found matching query: \"${query}\"`\n }\n\n // Get all folders to be able to show notebook names\n const folders = this.unwrap(\n await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n query: {\n fields: \"id,title\",\n },\n }),\n )\n\n // Create a map of folder IDs to folder titles for quick lookup\n const folderMap: Record<string, string> = {}\n folders.forEach((folder) => {\n folderMap[folder.id] = folder.title\n })\n\n // Format the search results\n const resultLines: string[] = []\n resultLines.push(`Found ${searchResults.items.length} notes matching query: \"${query}\"\\n`)\n resultLines.push(`NOTE: To read a notebook, use the notebook ID (not the note title)\\n`)\n\n // If multiple notes were found, add a hint about read_multinote\n if (searchResults.items.length > 1) {\n const noteIds = searchResults.items.map((note) => note.id)\n resultLines.push(`TIP: To read all ${searchResults.items.length} notes at once, use:\\n`)\n resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n }\n\n searchResults.items.forEach((note) => {\n const notebookTitle = folderMap[note.parent_id ?? \"\"] ?? \"Unknown notebook\"\n const notebookId = note.parent_id ?? \"unknown\"\n const updatedDate = this.formatDate(note.updated_time)\n\n resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n resultLines.push(` Notebook: \"${notebookTitle}\" (notebook_id: \"${notebookId}\")`)\n resultLines.push(` Updated: ${updatedDate}`)\n\n // Add a snippet of the note body if available\n if (note.body) {\n const snippet = note.body.substring(0, 100).replace(/\\n/g, \" \") + (note.body.length > 100 ? \"...\" : \"\")\n resultLines.push(` Snippet: ${snippet}`)\n }\n\n // Add hints for using related commands\n resultLines.push(` To read this note: read_note note_id=\"${note.id}\"`)\n resultLines.push(` To read this notebook: read_notebook notebook_id=\"${notebookId}\"`)\n\n resultLines.push(\"\") // Empty line between notes\n })\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n process.stderr.write(`searching notes error: ${error}\\n`)\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to search notes: ${message}`)\n }\n }\n}\n\nexport default SearchNotes\n","import axios, { type AxiosResponse } from \"axios\"\nimport type { Either } from \"functype\"\nimport { Left, Right } from \"functype\"\n\ntype JoplinAPIClientConfig = {\n host?: string\n port?: number\n token: string\n}\n\ntype JoplinAPIResponse<T = unknown> = {\n items: T[]\n has_more: boolean\n}\n\ntype RequestOptions = {\n query?: Record<string, unknown>\n [key: string]: unknown\n}\n\nclass JoplinAPIClient {\n private readonly baseURL: string\n private readonly token: string\n\n constructor({ host = \"127.0.0.1\", port = 41184, token }: JoplinAPIClientConfig) {\n this.baseURL = `http://${host}:${port}`\n this.token = token\n }\n\n async serviceAvailable(): Promise<Either<Error, true>> {\n try {\n const response: AxiosResponse<string> = await axios.get(`${this.baseURL}/ping`, { timeout: 5_000 })\n if (response.status === 200 && response.data === \"JoplinClipperServer\") {\n return Right(true as const)\n }\n return Left(new Error(\"Unexpected response from Joplin ping\"))\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async getAllItems<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T[]>> {\n const fetchPage = async (page: number, acc: T[]): Promise<T[]> => {\n const result = await this.get<JoplinAPIResponse<T>>(path, this.mergeRequestOptions(options, { query: { page } }))\n const response = result.fold(\n (err) => {\n throw err\n },\n (data) => data,\n )\n if (!Array.isArray(response.items)) {\n throw new Error(`Unexpected response format from Joplin API for path: ${path}`)\n }\n const combined = [...acc, ...response.items]\n return response.has_more ? fetchPage(page + 1, combined) : combined\n }\n\n try {\n return Right(await fetchPage(1, []))\n } catch (error: unknown) {\n process.stderr.write(`Error in getAllItems for path ${path}: ${error}\\n`)\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.get(`${this.baseURL}${path}`, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async post<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.post(`${this.baseURL}${path}`, body, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async delete<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.delete(`${this.baseURL}${path}`, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async put<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.put(`${this.baseURL}${path}`, body, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n private requestOptions(options: RequestOptions = {}): RequestOptions {\n return this.mergeRequestOptions(\n {\n query: { token: this.token },\n },\n options,\n )\n }\n\n private mergeRequestOptions(options1: RequestOptions, options2: RequestOptions): RequestOptions {\n return {\n query: {\n ...(options1.query ?? {}),\n ...(options2.query ?? {}),\n },\n ...this.except(options1, \"query\"),\n ...this.except(options2, \"query\"),\n }\n }\n\n private except(obj: Record<string, unknown>, key: string): Record<string, unknown> {\n const result = { ...obj }\n delete result[key]\n return result\n }\n}\n\nexport default JoplinAPIClient\nexport type { JoplinAPIClientConfig, JoplinAPIResponse, RequestOptions }\n","import { Either } from \"functype\"\n\nimport JoplinAPIClient from \"./lib/joplin-api-client.js\"\nimport type { JoplinSidecar } from \"./lib/joplin-sidecar.js\"\nimport {\n CreateFolder,\n CreateNote,\n DeleteFolder,\n DeleteNote,\n EditFolder,\n EditNote,\n ListNotebooks,\n ReadMultiNote,\n ReadNote,\n ReadNotebook,\n SearchNotes,\n} from \"./lib/tools/index.js\"\n\nexport type JoplinServerConfig = {\n host: string\n port: number\n token: string\n sidecar?: JoplinSidecar\n}\n\nexport class JoplinServerManager {\n private apiClient: JoplinAPIClient\n private config: JoplinServerConfig\n private connected: boolean = false\n private tools: {\n listNotebooks: ListNotebooks\n searchNotes: SearchNotes\n readNotebook: ReadNotebook\n readNote: ReadNote\n readMultiNote: ReadMultiNote\n createNote: CreateNote\n createFolder: CreateFolder\n editNote: EditNote\n editFolder: EditFolder\n deleteNote: DeleteNote\n deleteFolder: DeleteFolder\n }\n\n constructor(config: JoplinServerConfig) {\n this.config = config\n this.apiClient = new JoplinAPIClient({\n host: config.host,\n port: config.port,\n token: config.token,\n })\n\n this.tools = {\n listNotebooks: new ListNotebooks(this.apiClient),\n searchNotes: new SearchNotes(this.apiClient),\n readNotebook: new ReadNotebook(this.apiClient),\n readNote: new ReadNote(this.apiClient),\n readMultiNote: new ReadMultiNote(this.apiClient),\n createNote: new CreateNote(this.apiClient),\n createFolder: new CreateFolder(this.apiClient),\n editNote: new EditNote(this.apiClient),\n editFolder: new EditFolder(this.apiClient),\n deleteNote: new DeleteNote(this.apiClient),\n deleteFolder: new DeleteFolder(this.apiClient),\n }\n }\n\n async ensureConnected(): Promise<void> {\n if (this.connected) {\n const result = await this.apiClient.serviceAvailable()\n if (Either.isRight(result)) return\n this.connected = false\n }\n\n const available = await this.apiClient.serviceAvailable()\n if (Either.isRight(available)) {\n this.connected = true\n process.stderr.write(`Connected to Joplin at ${this.config.host}:${this.config.port}\\n`)\n return\n }\n\n // If sidecar exists, try starting it\n if (this.config.sidecar) {\n process.stderr.write(\"Joplin not available, starting sidecar...\\n\")\n const startResult = await this.config.sidecar.start()\n if (Either.isLeft(startResult)) {\n const error = startResult.fold(\n (e) => e,\n () => null as never,\n )\n throw new Error(`Sidecar failed [${error.code}]: ${error.message}`)\n }\n const retryAvailable = await this.apiClient.serviceAvailable()\n if (Either.isRight(retryAvailable)) {\n this.connected = true\n process.stderr.write(`Connected to Joplin via sidecar at ${this.config.host}:${this.config.port}\\n`)\n return\n }\n }\n\n throw new Error(\n `Joplin is not available at ${this.config.host}:${this.config.port}. ` +\n `Please ensure Joplin is running or configure a sidecar.`,\n )\n }\n\n // Tool execution methods\n async listNotebooks(): Promise<string> {\n await this.ensureConnected()\n return await this.tools.listNotebooks.call()\n }\n\n async searchNotes(query: string): Promise<string> {\n await this.ensureConnected()\n return await this.tools.searchNotes.call(query)\n }\n\n async readNotebook(notebookId: string): Promise<string> {\n await this.ensureConnected()\n return await this.tools.readNotebook.call(notebookId)\n }\n\n async readNote(noteId: string): Promise<string> {\n await this.ensureConnected()\n return await this.tools.readNote.call(noteId)\n }\n\n async readMultiNote(noteIds: string[]): Promise<string> {\n await this.ensureConnected()\n return await this.tools.readMultiNote.call(noteIds)\n }\n\n async createNote(params: {\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n image_data_url?: string | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.createNote.call(params)\n }\n\n async createFolder(params: { title: string; parent_id?: string | undefined }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.createFolder.call(params)\n }\n\n async editNote(params: {\n note_id: string\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n todo_completed?: boolean | undefined\n todo_due?: number | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.editNote.call(params)\n }\n\n async editFolder(params: {\n folder_id: string\n title?: string | undefined\n parent_id?: string | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.editFolder.call(params)\n }\n\n async deleteNote(params: { note_id: string; confirm?: boolean | undefined }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.deleteNote.call(params)\n }\n\n async deleteFolder(params: {\n folder_id: string\n confirm?: boolean | undefined\n force?: boolean | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.deleteFolder.call(params)\n }\n\n async sync(): Promise<string> {\n await this.ensureConnected()\n const desktopWarning = this.config.sidecar?.isDesktopDetected()\n ? \"\\n\\nNote: Joplin Desktop is also running. The sidecar and Desktop use separate databases. \" +\n \"Notes sync between them only if both are configured with the same sync target.\"\n : \"\"\n // Try triggering sync via the Joplin REST API (POST /services/sync)\n const result = await this.apiClient.post<Record<string, unknown>>(\"/services/sync\", { action: \"start\" })\n return result.fold(\n (error) => {\n const msg = error.message || String(error)\n // 404 or \"No action API\" = Joplin instance doesn't expose sync as a REST service\n // This is normal for Joplin Terminal CLI — it auto-syncs on its configured interval\n if (msg.includes(\"404\") || msg.includes(\"No action API\") || msg.includes(\"No such service\")) {\n return (\n `Sync is managed automatically by the Joplin server on its configured interval ` +\n `(default: every 5 minutes). On-demand sync is not available via the Joplin Terminal API.${desktopWarning}`\n )\n }\n return `Sync failed: ${msg}${desktopWarning}`\n },\n () => `Sync triggered successfully.${desktopWarning}`,\n )\n }\n}\n\nexport function initializeJoplinManager(config: JoplinServerConfig): JoplinServerManager {\n return new JoplinServerManager(config)\n}\n","import { FastMCP, UserError } from \"fastmcp\"\nimport { readFileSync } from \"fs\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { z } from \"zod\"\n\nimport { ToolError } from \"./lib/tools/index.js\"\nimport type { JoplinServerManager } from \"./server-core.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\n\n/**\n * Convert a ToolError thrown by a tool into a FastMCP UserError so the error\n * message reaches the agent verbatim with isError: true (instead of being\n * wrapped in FastMCP's generic \"Tool 'x' execution failed: ...\" envelope).\n */\nconst runWithUserError = async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn()\n } catch (e) {\n if (e instanceof ToolError) throw new UserError(e.message)\n throw e\n }\n}\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"))\nconst VERSION = packageJson.version\n\nexport type FastMCPServerOptions = {\n host: string\n port: number\n token: string\n httpPort?: number\n endpoint?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): { server: FastMCP; manager: JoplinServerManager } {\n process.stderr.write(\"Initializing FastMCP server for Joplin...\\n\")\n\n // Initialize Joplin manager\n const manager = initializeJoplinManager({ host: options.host, port: options.port, token: options.token })\n\n // Create FastMCP server\n const server = new FastMCP({\n name: \"joplin\",\n version: VERSION,\n health: {\n enabled: true,\n path: \"/health\",\n status: 200,\n message: JSON.stringify({\n status: \"healthy\",\n service: \"joplin-mcp-server\",\n version: VERSION,\n joplinPort: options.port,\n timestamp: new Date().toISOString(),\n }),\n },\n })\n\n // Add list_notebooks tool\n server.addTool({\n name: \"list_notebooks\",\n description: \"Retrieve the complete notebook hierarchy from Joplin\",\n parameters: z.object({}),\n execute: () => runWithUserError(() => manager.listNotebooks()),\n })\n\n // Add search_notes tool\n server.addTool({\n name: \"search_notes\",\n description: \"Search for notes in Joplin and return matching notebooks\",\n parameters: z.object({\n query: z.string().describe(\"Search query for notes\"),\n }),\n execute: (args) => runWithUserError(() => manager.searchNotes(args.query)),\n })\n\n // Add read_notebook tool\n server.addTool({\n name: \"read_notebook\",\n description: \"Read the contents of a specific notebook\",\n parameters: z.object({\n notebook_id: z.string().describe(\"ID of the notebook to read\"),\n }),\n execute: (args) => runWithUserError(() => manager.readNotebook(args.notebook_id)),\n })\n\n // Add read_note tool\n server.addTool({\n name: \"read_note\",\n description: \"Read the full content of a specific note\",\n parameters: z.object({\n note_id: z.string().describe(\"ID of the note to read\"),\n }),\n execute: (args) => runWithUserError(() => manager.readNote(args.note_id)),\n })\n\n // Add read_multinote tool\n server.addTool({\n name: \"read_multinote\",\n description: \"Read the full content of multiple notes at once\",\n parameters: z.object({\n note_ids: z.array(z.string()).describe(\"Array of note IDs to read\"),\n }),\n execute: (args) => runWithUserError(() => manager.readMultiNote(args.note_ids)),\n })\n\n // Add create_note tool\n server.addTool({\n name: \"create_note\",\n description: \"Create a new note in Joplin\",\n parameters: z.object({\n title: z.string().optional().describe(\"Note title\"),\n body: z.string().optional().describe(\"Note content in Markdown\"),\n body_html: z.string().optional().describe(\"Note content in HTML\"),\n parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n image_data_url: z.string().optional().describe(\"Base64 encoded image data URL\"),\n }),\n execute: (args) => runWithUserError(() => manager.createNote(args)),\n })\n\n // Add create_folder tool\n server.addTool({\n name: \"create_folder\",\n description: \"Create a new folder/notebook in Joplin\",\n parameters: z.object({\n title: z.string().describe(\"Notebook title\"),\n parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n }),\n execute: (args) => runWithUserError(() => manager.createFolder(args)),\n })\n\n // Add edit_note tool\n server.addTool({\n name: \"edit_note\",\n description: \"Edit/update an existing note in Joplin\",\n parameters: z.object({\n note_id: z.string().describe(\"ID of the note to edit\"),\n title: z.string().optional().describe(\"New note title\"),\n body: z.string().optional().describe(\"New note content in Markdown\"),\n body_html: z.string().optional().describe(\"New note content in HTML\"),\n parent_id: z.string().optional().describe(\"New parent notebook ID\"),\n is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n todo_completed: z.boolean().optional().describe(\"Whether todo is completed\"),\n todo_due: z.number().optional().describe(\"Todo due date (Unix timestamp)\"),\n }),\n execute: (args) => runWithUserError(() => manager.editNote(args)),\n })\n\n // Add edit_folder tool\n server.addTool({\n name: \"edit_folder\",\n description: \"Edit/update an existing folder/notebook in Joplin\",\n parameters: z.object({\n folder_id: z.string().describe(\"ID of the folder to edit\"),\n title: z.string().optional().describe(\"New folder title\"),\n parent_id: z.string().optional().describe(\"New parent folder ID\"),\n }),\n execute: (args) => runWithUserError(() => manager.editFolder(args)),\n })\n\n // Add delete_note tool\n server.addTool({\n name: \"delete_note\",\n description: \"Delete a note from Joplin (requires confirmation)\",\n parameters: z.object({\n note_id: z.string().describe(\"ID of the note to delete\"),\n confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n }),\n execute: (args) => runWithUserError(() => manager.deleteNote(args)),\n })\n\n // Add delete_folder tool\n server.addTool({\n name: \"delete_folder\",\n description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n parameters: z.object({\n folder_id: z.string().describe(\"ID of the folder to delete\"),\n confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n force: z.boolean().optional().describe(\"Force delete even if folder has contents\"),\n }),\n execute: (args) => runWithUserError(() => manager.deleteFolder(args)),\n })\n\n // Add sync tool\n server.addTool({\n name: \"sync\",\n description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n parameters: z.object({}),\n execute: () => runWithUserError(() => manager.sync()),\n })\n\n process.stderr.write(\"FastMCP server configured with 12 Joplin tools\\n\")\n return { server, manager }\n}\n\nexport async function startFastMCPServer(options: FastMCPServerOptions): Promise<void> {\n const { server } = createFastMCPServer(options)\n\n process.stderr.write(`Configured for Joplin at ${options.host}:${options.port}\\n`)\n\n const port = options.httpPort ?? 3000\n const endpoint = options.endpoint ?? \"/mcp\"\n\n await server.start({\n transportType: \"httpStream\",\n httpStream: {\n port,\n endpoint: endpoint as `/${string}`,\n },\n })\n\n process.stderr.write(`FastMCP server running on http://0.0.0.0:${port}${endpoint}\\n`)\n}\n","#!/usr/bin/env node\n\ndeclare const __VERSION__: string\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { type CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\"\nimport fs from \"fs\"\nimport path from \"path\"\nimport { fileURLToPath } from \"url\"\n\nimport { DEFAULT_API_PORT, JoplinSidecar, type SyncTarget } from \"./lib/joplin-sidecar.js\"\nimport parseArgs from \"./lib/parse-args.js\"\nimport { ToolError } from \"./lib/tools/index.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\nimport { startFastMCPServer } from \"./server-fastmcp.js\"\n\n// Parse command line arguments\nconst parsedArgs = parseArgs()\nconst { transport, httpPort, profileDir, syncTarget } = parsedArgs\n\nconst isHttpMode = transport === \"http\"\n\n// External mode: JOPLIN_HOST/JOPLIN_PORT set = connect directly, skip sidecar.\n// Normalize empty strings to undefined so `JOPLIN_HOST=` (a common way to\n// \"unset\" an inherited env var) does not trigger external mode with an empty host.\nconst externalHost =\n process.env.JOPLIN_HOST !== undefined && process.env.JOPLIN_HOST !== \"\" ? process.env.JOPLIN_HOST : undefined\nconst externalPort =\n process.env.JOPLIN_PORT !== undefined && process.env.JOPLIN_PORT !== \"\"\n ? parseInt(process.env.JOPLIN_PORT, 10)\n : undefined\nconst externalMode = externalHost !== undefined || externalPort !== undefined\n\n// Token is required for external mode, auto-generated for sidecar mode\nif (!process.env.JOPLIN_TOKEN && externalMode) {\n process.stderr.write(\n \"Error: JOPLIN_TOKEN is required in external mode. Use --token <token> or set JOPLIN_TOKEN environment variable.\\n\",\n )\n process.exit(1)\n}\n\n// In sidecar mode, persist the auto-generated token so the config hash stays stable\n// across restarts, enabling config caching to skip redundant `joplin config` CLI calls.\nconst joplinToken = (() => {\n if (process.env.JOPLIN_TOKEN) return process.env.JOPLIN_TOKEN\n if (externalMode) return `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n const tokenPath = path.join(profileDir, \".mcp-token\")\n try {\n const saved = fs.readFileSync(tokenPath, \"utf-8\").trim()\n if (saved) return saved\n } catch {\n // No saved token yet\n }\n const token = `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n try {\n fs.mkdirSync(profileDir, { recursive: true })\n fs.writeFileSync(tokenPath, token)\n } catch {\n // Non-critical — token still works, just won't be cached\n }\n return token\n})()\n\nasync function setupConnection(): Promise<{ host: string; port: number; sidecar: JoplinSidecar | undefined }> {\n if (externalMode) {\n // External mode — connect to existing Joplin instance (e.g. Windows desktop from WSL)\n const host = externalHost ?? \"127.0.0.1\"\n const port = externalPort ?? DEFAULT_API_PORT\n process.stderr.write(`External mode: connecting to Joplin at ${host}:${port}\\n`)\n return { host, port, sidecar: undefined }\n }\n\n // Sidecar mode — spawn and manage Joplin Terminal\n const sidecar = new JoplinSidecar({\n profileDir,\n apiPort: DEFAULT_API_PORT,\n apiToken: joplinToken,\n syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n })\n\n // Phase 1: Resolve port (fast — a few HTTP probes).\n // Must complete before getPort() so downstream gets the correct port.\n const portResult = await sidecar.resolvePort()\n portResult.fold(\n (err) => process.stderr.write(`Warning: Port resolution failed: ${err.message}\\n`),\n (p) => process.stderr.write(`Sidecar will use port ${p}\\n`),\n )\n\n // Phase 2: Fire-and-forget the slow startup (CLI config, spawn, wait).\n // ensureConnected() in server-core.ts will await or retry on first tool call.\n void sidecar.start().then((result) => {\n result.fold(\n (err) => {\n process.stderr.write(`Warning: Sidecar failed to start: ${err.message}\\n`)\n process.stderr.write(\"Attempting to connect to existing Joplin instance...\\n\")\n },\n () => {\n process.stderr.write(\"Joplin sidecar started successfully\\n\")\n },\n )\n })\n\n // Cleanup on exit\n const cleanup = async () => {\n await sidecar.stop()\n process.exit(0)\n }\n process.on(\"SIGINT\", () => void cleanup())\n process.on(\"SIGTERM\", () => void cleanup())\n\n return { host: sidecar.getHost(), port: sidecar.getPort(), sidecar }\n}\n\n// Main startup logic\nasync function main(): Promise<void> {\n const { host, port, sidecar } = await setupConnection()\n\n if (isHttpMode) {\n process.stderr.write(\"Starting HTTP transport mode with FastMCP...\\n\")\n await startFastMCPServer({\n host,\n port,\n token: joplinToken,\n httpPort,\n endpoint: \"/mcp\",\n })\n } else {\n process.stderr.write(\"Starting stdio transport mode...\\n\")\n await startStdioServer(host, port, joplinToken, sidecar)\n }\n}\n\nmain().catch((error) => {\n process.stderr.write(`Failed to start MCP server: ${error}\\n`)\n process.exit(1)\n})\n\nasync function startStdioServer(host: string, port: number, token: string, sidecar?: JoplinSidecar): Promise<void> {\n const manager = initializeJoplinManager({ host, port, token, sidecar })\n\n const server = new Server(\n {\n name: \"joplin-mcp-server\",\n version: __VERSION__,\n },\n {\n capabilities: {\n resources: {},\n tools: {},\n prompts: {},\n },\n },\n )\n\n // Register tool list handler\n server.setRequestHandler(ListToolsRequestSchema, () => {\n return {\n tools: [\n {\n name: \"list_notebooks\",\n description: \"Retrieve the complete notebook hierarchy from Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n },\n {\n name: \"search_notes\",\n description: \"Search for notes in Joplin and return matching notebooks\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"read_notebook\",\n description: \"Read the contents of a specific notebook\",\n inputSchema: {\n type: \"object\",\n properties: {\n notebook_id: { type: \"string\", description: \"ID of the notebook to read\" },\n },\n required: [\"notebook_id\"],\n },\n },\n {\n name: \"read_note\",\n description: \"Read the full content of a specific note\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_id: { type: \"string\", description: \"ID of the note to read\" },\n },\n required: [\"note_id\"],\n },\n },\n {\n name: \"read_multinote\",\n description: \"Read the full content of multiple notes at once\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_ids: { type: \"array\", items: { type: \"string\" }, description: \"Array of note IDs to read\" },\n },\n required: [\"note_ids\"],\n },\n },\n {\n name: \"create_note\",\n description: \"Create a new note in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Note title\" },\n body: { type: \"string\", description: \"Note content in Markdown\" },\n body_html: { type: \"string\", description: \"Note content in HTML\" },\n parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n image_data_url: { type: \"string\", description: \"Base64 encoded image data URL\" },\n },\n },\n },\n {\n name: \"create_folder\",\n description: \"Create a new folder/notebook in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Notebook title\" },\n parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"edit_note\",\n description: \"Edit/update an existing note in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_id: { type: \"string\", description: \"ID of the note to edit\" },\n title: { type: \"string\", description: \"New note title\" },\n body: { type: \"string\", description: \"New note content in Markdown\" },\n body_html: { type: \"string\", description: \"New note content in HTML\" },\n parent_id: { type: \"string\", description: \"New parent notebook ID\" },\n is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n todo_completed: { type: \"boolean\", description: \"Whether todo is completed\" },\n todo_due: { type: \"number\", description: \"Todo due date (Unix timestamp)\" },\n },\n required: [\"note_id\"],\n },\n },\n {\n name: \"edit_folder\",\n description: \"Edit/update an existing folder/notebook in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n folder_id: { type: \"string\", description: \"ID of the folder to edit\" },\n title: { type: \"string\", description: \"New folder title\" },\n parent_id: { type: \"string\", description: \"New parent folder ID\" },\n },\n required: [\"folder_id\"],\n },\n },\n {\n name: \"delete_note\",\n description: \"Delete a note from Joplin (requires confirmation)\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_id: { type: \"string\", description: \"ID of the note to delete\" },\n confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n },\n required: [\"note_id\"],\n },\n },\n {\n name: \"delete_folder\",\n description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n inputSchema: {\n type: \"object\",\n properties: {\n folder_id: { type: \"string\", description: \"ID of the folder to delete\" },\n confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n force: { type: \"boolean\", description: \"Force delete even if folder has contents\" },\n },\n required: [\"folder_id\"],\n },\n },\n {\n name: \"sync\",\n description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n },\n ],\n }\n })\n\n // Register tool call handler\n server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const toolName = request.params.name\n const args = request.params.arguments ?? {}\n\n try {\n switch (toolName) {\n case \"list_notebooks\": {\n const listResult = await manager.listNotebooks()\n return { content: [{ type: \"text\", text: listResult }], isError: false }\n }\n\n case \"search_notes\": {\n const searchResult = await manager.searchNotes(args.query as string)\n return { content: [{ type: \"text\", text: searchResult }], isError: false }\n }\n\n case \"read_notebook\": {\n const notebookResult = await manager.readNotebook(args.notebook_id as string)\n return { content: [{ type: \"text\", text: notebookResult }], isError: false }\n }\n\n case \"read_note\": {\n const noteResult = await manager.readNote(args.note_id as string)\n return { content: [{ type: \"text\", text: noteResult }], isError: false }\n }\n\n case \"read_multinote\": {\n const multiResult = await manager.readMultiNote(args.note_ids as string[])\n return { content: [{ type: \"text\", text: multiResult }], isError: false }\n }\n\n case \"create_note\": {\n const createNoteResult = await manager.createNote(\n args as {\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n image_data_url?: string | undefined\n },\n )\n return { content: [{ type: \"text\", text: createNoteResult }], isError: false }\n }\n\n case \"create_folder\": {\n const createFolderResult = await manager.createFolder(\n args as {\n title: string\n parent_id?: string | undefined\n },\n )\n return { content: [{ type: \"text\", text: createFolderResult }], isError: false }\n }\n\n case \"edit_note\": {\n const editNoteResult = await manager.editNote(\n args as {\n note_id: string\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n todo_completed?: boolean | undefined\n todo_due?: number | undefined\n },\n )\n return { content: [{ type: \"text\", text: editNoteResult }], isError: false }\n }\n\n case \"edit_folder\": {\n const editFolderResult = await manager.editFolder(\n args as {\n folder_id: string\n title?: string | undefined\n parent_id?: string | undefined\n },\n )\n return { content: [{ type: \"text\", text: editFolderResult }], isError: false }\n }\n\n case \"delete_note\": {\n const deleteNoteResult = await manager.deleteNote(\n args as {\n note_id: string\n confirm?: boolean | undefined\n },\n )\n return { content: [{ type: \"text\", text: deleteNoteResult }], isError: false }\n }\n\n case \"delete_folder\": {\n const deleteFolderResult = await manager.deleteFolder(\n args as {\n folder_id: string\n confirm?: boolean | undefined\n force?: boolean | undefined\n },\n )\n return { content: [{ type: \"text\", text: deleteFolderResult }], isError: false }\n }\n\n case \"sync\": {\n const syncResult = await manager.sync()\n return { content: [{ type: \"text\", text: syncResult }], isError: false }\n }\n\n default:\n throw new Error(`Unknown tool: ${toolName}`)\n }\n } catch (error) {\n // ToolError already carries a user-facing message; pass it through verbatim.\n // For unexpected errors, prefix with \"Error:\" to make the failure obvious.\n const errorMessage = error instanceof Error ? error.message : String(error)\n const text = error instanceof ToolError ? errorMessage : `Error: ${errorMessage}`\n return {\n content: [{ type: \"text\", text }],\n isError: true,\n }\n }\n })\n\n // Create logs directory if it doesn't exist\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = path.dirname(__filename)\n const logsDir = path.join(__dirname, \"..\", \"logs\")\n\n if (!fs.existsSync(logsDir)) {\n fs.mkdirSync(logsDir, { recursive: true })\n }\n\n // Create a log file for this session\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n const logFile = path.join(logsDir, `mcp-server-${timestamp}.log`)\n\n // Create a custom transport wrapper to log commands and responses\n class LoggingTransport extends StdioServerTransport {\n private commandCounter: number\n\n constructor() {\n super()\n this.commandCounter = 0\n }\n\n async sendMessage(message: unknown): Promise<void> {\n const logEntry = {\n timestamp: new Date().toISOString(),\n direction: \"RESPONSE\",\n message,\n }\n\n fs.appendFileSync(logFile, `${JSON.stringify(logEntry)}\\n`)\n\n const parent = Object.getPrototypeOf(Object.getPrototypeOf(this)) as {\n sendMessage: (m: unknown) => Promise<void>\n }\n await parent.sendMessage.call(this, message)\n }\n\n async handleMessage(message: unknown): Promise<void> {\n this.commandCounter++\n const logEntry = {\n timestamp: new Date().toISOString(),\n direction: \"COMMAND\",\n commandNumber: this.commandCounter,\n message,\n }\n\n fs.appendFileSync(logFile, `${JSON.stringify(logEntry)}\\n`)\n\n const parent = Object.getPrototypeOf(Object.getPrototypeOf(this)) as {\n handleMessage: (m: unknown) => Promise<void>\n }\n await parent.handleMessage.call(this, message)\n }\n }\n\n const stdioTransport = new LoggingTransport()\n\n try {\n await server.connect(stdioTransport)\n process.stderr.write(\"MCP server started and ready to receive commands\\n\")\n } catch (error: unknown) {\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["crypto"],"sources":["../src/lib/joplin-sidecar.ts","../src/lib/parse-args.ts","../src/lib/resolve-token.ts","../src/lib/tools/base-tool.ts","../src/lib/tools/create-folder.ts","../src/lib/tools/create-note.ts","../src/lib/tools/delete-folder.ts","../src/lib/tools/delete-note.ts","../src/lib/tools/edit-folder.ts","../src/lib/tools/edit-note.ts","../src/lib/tools/list-notebooks.ts","../src/lib/tools/read-multi-note.ts","../src/lib/tools/read-note.ts","../src/lib/tools/read-notebook.ts","../src/lib/tools/search-notes.ts","../src/lib/joplin-api-client.ts","../src/server-core.ts","../src/server-fastmcp.ts","../src/index.ts"],"sourcesContent":["import { type ChildProcess, exec, execFile, spawn } from \"child_process\"\nimport crypto from \"crypto\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport { Platform } from \"functype-os\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { promisify } from \"util\"\n\nconst execAsync = promisify(exec)\nconst execFileAsync = promisify(execFile)\n\nconst isWindows = Platform.isWindows()\nconst whichCmd = isWindows ? \"where\" : \"which\"\n\nexport const DEFAULT_API_PORT = 41184\nconst MAX_PORT_ATTEMPTS = 10\n\nexport type SyncTarget =\n | { type: \"none\" }\n | { type: \"filesystem\"; path: string }\n | { type: \"joplin-cloud\"; email: string; password: string }\n | { type: \"joplin-server\"; url: string; email: string; password: string }\n | { type: \"webdav\"; url: string; username: string; password: string }\n | { type: \"nextcloud\"; url: string; username: string; password: string }\n | { type: \"s3\"; bucket: string; region: string; accessKey: string; secretKey: string }\n | { type: \"dropbox\" }\n | { type: \"onedrive\" }\n\nexport type SidecarConfig = {\n profileDir: string\n apiPort: number\n apiToken: string\n syncTarget?: SyncTarget\n syncInterval?: number\n // Identifies the release that spawned a sidecar. A running server started by a\n // different version is replaced rather than reused, so upgrades take effect.\n version?: string\n}\n\nexport type SidecarError = {\n code:\n | \"CLI_NOT_FOUND\"\n | \"CONFIG_FAILED\"\n | \"SPAWN_FAILED\"\n | \"HEALTH_CHECK_FAILED\"\n | \"STOP_FAILED\"\n | \"SYNC_FAILED\"\n | \"PORT_CONFLICT\"\n | \"PORT_OCCUPIED\"\n | \"PORT_EXHAUSTED\"\n message: string\n cause?: unknown\n}\n\nconst sidecarError = (code: SidecarError[\"code\"], message: string, cause?: unknown): SidecarError => ({\n code,\n message,\n cause,\n})\n\nconst syncTargetId = (target: SyncTarget): number =>\n Match(target.type)\n .case(\"none\", () => 0)\n .case(\"filesystem\", () => 2)\n .case(\"webdav\", () => 6)\n .case(\"nextcloud\", () => 5)\n .case(\"dropbox\", () => 7)\n .case(\"onedrive\", () => 3)\n .case(\"s3\", () => 8)\n .case(\"joplin-server\", () => 9)\n .case(\"joplin-cloud\", () => 10)\n .default(() => 0)\n\nconst fetchWithTimeout = async (url: string, timeoutMs: number = 5_000): Promise<Response> => {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { signal: controller.signal })\n } finally {\n clearTimeout(timer)\n }\n}\n\ntype PortProbeResult =\n | \"free\"\n | \"joplin_ours\"\n | \"joplin_stale\"\n | \"joplin_foreign\"\n | \"joplin_unowned\"\n | \"occupied_other\"\n | \"occupied_unresponsive\"\n\nconst CLIPPER_PID_FILE = \"clipper-pid.txt\"\n\nconst readProfileServerPid = (profileDir: string): Option<number> => {\n try {\n const raw = fs.readFileSync(join(profileDir, CLIPPER_PID_FILE), \"utf-8\").trim()\n const pid = Number.parseInt(raw, 10)\n return Number.isInteger(pid) && pid > 0 ? Option(pid) : Option.none()\n } catch {\n return Option.none()\n }\n}\n\nconst isProcessAlive = (pid: number): boolean => {\n try {\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n\n// A Joplin server records its PID inside the profile directory it serves. If our\n// profile has no live PID recorded, then whatever is answering on the port belongs\n// to some other profile — reusing it would silently serve the wrong database.\nconst servesOurProfile = (profileDir: string): boolean =>\n readProfileServerPid(profileDir).fold(\n () => false,\n (pid) => isProcessAlive(pid),\n )\n\nconst SIDECAR_STAMP_FILE = \".mcp-sidecar.json\"\n\nexport type SidecarStamp = {\n version: string\n identity: string\n}\n\n// Identity captures only what the reuse auth-probe does NOT already verify: the\n// release version and the sync configuration. Token compatibility is proven earlier\n// by the probe (a server holding a different token is rejected as foreign before\n// identity is ever compared), so the token is deliberately excluded — including it\n// would be redundant and would write token-derived data into the on-disk stamp. The\n// port is excluded too, being resolved dynamically and saying nothing about the\n// running server.\nconst computeIdentityHash = (config: SidecarConfig): string =>\n crypto\n .createHash(\"sha256\")\n .update(\n JSON.stringify({\n version: config.version ?? \"unknown\",\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n }),\n )\n .digest(\"hex\")\n .slice(0, 16)\n\nconst readSidecarStamp = (profileDir: string): Option<SidecarStamp> => {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(join(profileDir, SIDECAR_STAMP_FILE), \"utf-8\"))\n const stamp = raw as Partial<SidecarStamp>\n if (typeof stamp.version !== \"string\" || typeof stamp.identity !== \"string\") return Option.none()\n return Option({ version: stamp.version, identity: stamp.identity })\n } catch {\n return Option.none()\n }\n}\n\nconst writeSidecarStamp = (profileDir: string, stamp: SidecarStamp): void => {\n try {\n fs.writeFileSync(join(profileDir, SIDECAR_STAMP_FILE), JSON.stringify(stamp))\n } catch {\n // Non-critical — worst case the next start replaces a reusable server\n }\n}\n\nexport type ReuseVerdict = \"reusable\" | \"stale\" | \"not-ours\"\n\n// Decides whether a Joplin server already serving our profile can be adopted.\n// An unstamped server predates stamping, so it is replaced rather than trusted.\nconst inspectRunningSidecar = (profileDir: string, identity: string): ReuseVerdict => {\n if (!servesOurProfile(profileDir)) return \"not-ours\"\n return readSidecarStamp(profileDir).fold(\n () => \"stale\",\n (stamp) => (stamp.identity === identity ? \"reusable\" : \"stale\"),\n )\n}\n\nconst isConnectionRefused = (err: unknown): boolean => {\n const e = err as { cause?: { code?: string; errors?: Array<{ code?: string }> } }\n if (e.cause?.code === \"ECONNREFUSED\") return true\n if (e.cause?.errors?.some((inner) => inner.code === \"ECONNREFUSED\") === true) return true\n return false\n}\n\nconst probePort = async (\n port: number,\n token: string,\n profileDir: string,\n identity: string,\n): Promise<PortProbeResult> => {\n try {\n const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`, 3_000)\n const body = await pingResponse.text()\n if (body !== \"JoplinClipperServer\") return \"occupied_other\"\n\n // It's a Joplin server — check if the token matches\n try {\n const authResponse = await fetchWithTimeout(\n `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n 3_000,\n )\n // Accepting our token is not enough: an orphan serving a different profile\n // answers identically but exposes the wrong (often empty) database, and a\n // server left by an older release serves the old code until it is replaced.\n if (authResponse.ok) {\n return Match(inspectRunningSidecar(profileDir, identity))\n .case(\"reusable\", () => \"joplin_ours\" as const)\n .case(\"stale\", () => \"joplin_stale\" as const)\n .default(() => \"joplin_unowned\" as const)\n }\n return \"joplin_foreign\"\n } catch {\n return \"joplin_foreign\"\n }\n } catch (err: unknown) {\n // ECONNREFUSED = nothing listening on the port = genuinely free\n if (isConnectionRefused(err)) return \"free\"\n // Timeout or other error = port is occupied but not responding to HTTP\n return \"occupied_unresponsive\"\n }\n}\n\ntype PortResolution =\n | { outcome: \"reuse_existing\"; port: number; desktopDetected: boolean }\n | { outcome: \"replace_stale\"; port: number; desktopDetected: boolean }\n | { outcome: \"free\"; port: number; desktopDetected: boolean }\n | { outcome: \"exhausted\" }\n\nconst resolveAvailablePort = async (\n startPort: number,\n token: string,\n profileDir: string,\n identity: string,\n): Promise<PortResolution> => {\n const tryPort = async (port: number, desktopDetected: boolean): Promise<PortResolution> => {\n if (port >= startPort + MAX_PORT_ATTEMPTS) return { outcome: \"exhausted\" }\n const status = await probePort(port, token, profileDir, identity)\n if (status === \"free\") return { outcome: \"free\", port, desktopDetected }\n if (status === \"joplin_ours\") return { outcome: \"reuse_existing\", port, desktopDetected }\n if (status === \"joplin_stale\") return { outcome: \"replace_stale\", port, desktopDetected }\n if (status === \"joplin_unowned\") {\n process.stderr.write(\n `[joplin-sidecar] Port ${port}: Joplin server accepts our token but serves a different profile ` +\n `(likely an orphan from a previous run), skipping\\n`,\n )\n return tryPort(port + 1, desktopDetected)\n }\n if (status === \"joplin_foreign\") {\n process.stderr.write(\n `[joplin-sidecar] Port ${port}: another Joplin instance with a different token ` +\n `(possibly Joplin Desktop), skipping\\n`,\n )\n return tryPort(port + 1, true)\n }\n process.stderr.write(`[joplin-sidecar] Port ${port} occupied (${status}), trying next...\\n`)\n return tryPort(port + 1, desktopDetected)\n }\n return tryPort(startPort, false)\n}\n\nconst CLI_BIN_NAME = isWindows ? \"joplin.cmd\" : \"joplin\"\nconst CLI_SEARCH_DEPTH = 5\n\n// Where a bundled Joplin CLI might live, nearest first. Resolution has to be\n// module-relative: under a packaged install the working directory belongs to the\n// host application, so process.cwd() finds nothing. It stays in the list last so\n// a plain `pnpm dev` from the repo root keeps working.\nconst ancestorDirs = (dir: string, remaining: number): string[] => {\n const parent = dirname(dir)\n if (remaining <= 0 || parent === dir) return [dir]\n return [dir, ...ancestorDirs(parent, remaining - 1)]\n}\n\nconst localCliCandidates = (): string[] =>\n [...ancestorDirs(dirname(fileURLToPath(import.meta.url)), CLI_SEARCH_DEPTH), process.cwd()].map((root) =>\n join(root, \"node_modules\", \".bin\", CLI_BIN_NAME),\n )\n\nconst findJoplinCli = async (): Promise<Either<SidecarError, string>> => {\n // 1. User override via env var\n const envCli = process.env.JOPLIN_CLI\n if (envCli) {\n if (fs.existsSync(envCli)) return Right(envCli)\n return Left(sidecarError(\"CLI_NOT_FOUND\", `JOPLIN_CLI path not found: ${envCli}`))\n }\n\n // 2. Bundled in node_modules, resolved relative to this module\n const bundled = localCliCandidates().find((candidate) => fs.existsSync(candidate))\n if (bundled) return Right(bundled)\n\n // 3. Global install\n try {\n const { stdout } = await execAsync(`${whichCmd} joplin`, { encoding: \"utf-8\", timeout: 10_000 })\n const joplinPath = stdout.trim().split(\"\\n\")[0]\n return Right(joplinPath)\n } catch {\n // not found\n }\n\n // 4. npx fallback (auto-downloads on first run)\n try {\n const { stdout } = await execAsync(`${whichCmd} npx`, { encoding: \"utf-8\", timeout: 10_000 })\n const npxPath = stdout.trim().split(\"\\n\")[0]\n process.stderr.write(\"[joplin-sidecar] No local joplin found, using npx (may download on first run)\\n\")\n return Right(npxPath)\n } catch {\n // not found\n }\n\n return Left(\n sidecarError(\n \"CLI_NOT_FOUND\",\n \"Joplin CLI not found. Install with: npm install -g joplin, or set JOPLIN_CLI=/path/to/joplin\",\n ),\n )\n}\n\nconst buildSettingsRecord = (config: SidecarConfig): Record<string, string> => {\n const settings: Record<string, string> = {\n \"api.token\": config.apiToken,\n \"api.port\": String(config.apiPort),\n }\n\n const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n settings[\"sync.target\"] = String(syncTargetId(syncTarget))\n\n if (syncTarget.type === \"filesystem\") {\n settings[\"sync.2.path\"] = syncTarget.path\n } else if (syncTarget.type === \"webdav\") {\n settings[\"sync.6.path\"] = syncTarget.url\n settings[\"sync.6.username\"] = syncTarget.username\n settings[\"sync.6.password\"] = syncTarget.password\n } else if (syncTarget.type === \"nextcloud\") {\n settings[\"sync.5.path\"] = syncTarget.url\n settings[\"sync.5.username\"] = syncTarget.username\n settings[\"sync.5.password\"] = syncTarget.password\n } else if (syncTarget.type === \"s3\") {\n settings[\"sync.8.path\"] = syncTarget.bucket\n settings[\"sync.8.region\"] = syncTarget.region\n settings[\"sync.8.username\"] = syncTarget.accessKey\n settings[\"sync.8.password\"] = syncTarget.secretKey\n } else if (syncTarget.type === \"joplin-server\") {\n settings[\"sync.9.path\"] = syncTarget.url\n settings[\"sync.9.username\"] = syncTarget.email\n settings[\"sync.9.password\"] = syncTarget.password\n } else if (syncTarget.type === \"joplin-cloud\") {\n settings[\"sync.10.username\"] = syncTarget.email\n settings[\"sync.10.password\"] = syncTarget.password\n }\n\n const interval = Option(config.syncInterval).orElse(300)\n settings[\"sync.interval\"] = String(interval)\n\n return settings\n}\n\nconst runJoplinConfig = async (cli: string, profileDir: string, key: string, value: string): Promise<void> => {\n const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n // --profile is a global flag: it must precede the subcommand or the CLI\n // silently ignores it and falls back to the default ~/.config/joplin profile.\n const args = cli.endsWith(\"npx\")\n ? [\"joplin\", \"--profile\", profileDir, \"config\", key, value]\n : [\"--profile\", profileDir, \"config\", key, value]\n await execFileAsync(cmd, args, { encoding: \"utf-8\", timeout: 30_000, shell: isWindows })\n}\n\nconst configureJoplin = async (cli: string, config: SidecarConfig): Promise<Either<SidecarError, void>> => {\n const settings = buildSettingsRecord(config)\n try {\n fs.mkdirSync(config.profileDir, { recursive: true })\n for (const [key, value] of Object.entries(settings)) {\n await runJoplinConfig(cli, config.profileDir, key, value)\n }\n return Right(undefined as void)\n } catch (e) {\n return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to configure Joplin via CLI\", e))\n }\n}\n\nconst spawnServer = (cli: string, config: SidecarConfig): Either<SidecarError, ChildProcess> => {\n try {\n const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n // --profile must precede the subcommand (see runJoplinConfig).\n const args = cli.endsWith(\"npx\")\n ? [\"joplin\", \"--profile\", config.profileDir, \"server\", \"start\"]\n : [\"--profile\", config.profileDir, \"server\", \"start\"]\n\n const proc = spawn(cmd, args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: false,\n shell: isWindows,\n })\n\n proc.stderr.on(\"data\", (data: Buffer) => {\n process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n })\n\n proc.stdout.on(\"data\", (data: Buffer) => {\n process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n })\n\n proc.on(\"error\", (err) => {\n process.stderr.write(`[joplin-sidecar] Process error: ${err.message}\\n`)\n })\n\n return Right(proc as ChildProcess)\n } catch (e) {\n return Left(sidecarError(\"SPAWN_FAILED\", \"Failed to spawn Joplin server process\", e))\n }\n}\n\nconst waitForReady = async (\n port: number,\n token: string,\n proc: ChildProcess | null,\n maxRetries: number = 30,\n intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n const deadline = Date.now() + 60_000\n\n // Track if the spawned process exits early (e.g., port already taken).\n // Mutable object container keeps the binding `const` while allowing the\n // exit handler to record the code asynchronously.\n const procExitState: { code: number | null } = { code: null }\n if (proc) {\n proc.once(\"exit\", (code) => {\n procExitState.code = code\n })\n }\n\n const attempt = async (i: number): Promise<Either<SidecarError, true>> => {\n if (i >= maxRetries || Date.now() > deadline) {\n return Left(sidecarError(\"HEALTH_CHECK_FAILED\", \"Joplin server did not become ready within 60s\"))\n }\n\n if (procExitState.code !== null) {\n return Left(\n sidecarError(\n \"SPAWN_FAILED\",\n `Joplin server process exited unexpectedly (code ${procExitState.code}). ` +\n `Port ${port} may already be in use by another Joplin instance or process.`,\n ),\n )\n }\n\n try {\n const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`)\n if (pingResponse.ok) {\n try {\n const authResponse = await fetchWithTimeout(\n `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n )\n if (authResponse.ok) {\n process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n return Right(true as const)\n }\n process.stderr.write(\n `[joplin-sidecar] Ping OK but auth failed (status ${authResponse.status}), retrying...\\n`,\n )\n } catch {\n process.stderr.write(\"[joplin-sidecar] Ping OK but auth check timed out, retrying...\\n\")\n }\n }\n } catch {\n // Not ready yet\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n return attempt(i + 1)\n }\n\n return attempt(0)\n}\n\nconst CONFIG_CACHE_FILE = \".mcp-configured\"\n\nconst computeConfigHash = (config: SidecarConfig): string => {\n const hashData = {\n apiPort: config.apiPort,\n apiToken: config.apiToken,\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n }\n return crypto.createHash(\"sha256\").update(JSON.stringify(hashData)).digest(\"hex\").slice(0, 16)\n}\n\nconst isConfigCached = (profileDir: string, hash: string): boolean => {\n try {\n const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n if (!fs.existsSync(cachePath)) return false\n const cached = JSON.parse(fs.readFileSync(cachePath, \"utf-8\"))\n return cached.hash === hash\n } catch {\n return false\n }\n}\n\nconst writeConfigCache = (profileDir: string, hash: string): void => {\n try {\n const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n fs.writeFileSync(cachePath, JSON.stringify({ hash, timestamp: Date.now() }))\n } catch {\n // Non-critical — skip silently\n }\n}\n\nexport { computeIdentityHash, inspectRunningSidecar, isProcessAlive, localCliCandidates, readProfileServerPid }\nexport { readSidecarStamp, servesOurProfile, writeSidecarStamp }\n\nexport class JoplinSidecar {\n private config: SidecarConfig\n private childProcess: ChildProcess | null = null\n private startPromise: Promise<Either<SidecarError, ChildProcess>> | null = null\n private portResolution: PortResolution | null = null\n private cleanupRegistered = false\n\n constructor(config: Partial<SidecarConfig> & { apiToken: string }) {\n this.config = {\n profileDir: config.profileDir ?? join(Platform.homeDir(), \".config\", \"joplin-mcp\"),\n apiPort: config.apiPort ?? DEFAULT_API_PORT,\n apiToken: config.apiToken,\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n version: config.version,\n }\n }\n\n private identity(): string {\n return computeIdentityHash(this.config)\n }\n\n // True while a Joplin server is serving our profile — whether we spawned it or\n // adopted one already running.\n private sidecarAlive(): boolean {\n return servesOurProfile(this.config.profileDir)\n }\n\n // A server left by a different release or a changed sync configuration is torn\n // down so the current one can take the port. This is how upgrades roll over: a\n // new version never adopts the previous version's running server.\n private async replaceStaleServer(port: number): Promise<void> {\n const previous = readSidecarStamp(this.config.profileDir).fold(\n () => \"unstamped\",\n (s) => `v${s.version}`,\n )\n process.stderr.write(\n `[joplin-sidecar] Replacing sidecar on port ${port} (${previous} -> v${this.config.version ?? \"unknown\"} ` +\n `or changed sync config)\\n`,\n )\n this.reapRecordedServer()\n await this.waitForPortFree(port)\n }\n\n private async waitForPortFree(port: number, maxAttempts: number = 20): Promise<void> {\n const attempt = async (i: number): Promise<void> => {\n if (i >= maxAttempts) {\n process.stderr.write(`[joplin-sidecar] Port ${port} still busy after teardown, continuing anyway\\n`)\n return\n }\n try {\n await fetchWithTimeout(`http://127.0.0.1:${port}/ping`, 1_000)\n } catch (err: unknown) {\n if (isConnectionRefused(err)) return\n }\n await new Promise((resolve) => setTimeout(resolve, 250))\n return attempt(i + 1)\n }\n return attempt(0)\n }\n\n // Best-effort synchronous teardown. Only sync work is possible from an \"exit\"\n // handler, and we reap the profile's recorded PID as well as our own child so\n // that a server we lost the handle to does not survive as an orphan.\n private killChildSync(): void {\n // Only reap what we spawned. A server we merely reused may be shared with\n // another MCP process, so tearing it down here is not ours to do.\n if (!this.childProcess) return\n try {\n this.childProcess.kill(\"SIGTERM\")\n } catch {\n // already gone\n }\n this.reapRecordedServer()\n }\n\n // Under the npx launcher our direct child is only a wrapper — the real Joplin\n // server is a grandchild that outlives it. Reap it via the PID it recorded in\n // the profile, which is how orphans accumulated across sessions.\n private reapRecordedServer(): void {\n readProfileServerPid(this.config.profileDir).fold(\n () => undefined,\n (pid) => {\n if (pid !== process.pid && isProcessAlive(pid)) {\n try {\n process.kill(pid, \"SIGTERM\")\n } catch {\n // already gone\n }\n }\n return undefined\n },\n )\n }\n\n // SIGINT/SIGTERM are handled by the entrypoint; these cover the exit paths it\n // misses. A SIGKILLed parent runs no handler at all, which is why start-up\n // refuses to reuse a server that is not serving our profile.\n private registerCleanup(): void {\n if (this.cleanupRegistered) return\n this.cleanupRegistered = true\n process.once(\"exit\", () => this.killChildSync())\n process.once(\"SIGHUP\", () => {\n this.killChildSync()\n process.exit(0)\n })\n }\n\n async resolvePort(): Promise<Either<SidecarError, number>> {\n const resolution = await resolveAvailablePort(\n this.config.apiPort,\n this.config.apiToken,\n this.config.profileDir,\n this.identity(),\n )\n this.portResolution = resolution\n if (resolution.outcome === \"exhausted\") {\n const lastPort = this.config.apiPort + MAX_PORT_ATTEMPTS - 1\n return Left(\n sidecarError(\n \"PORT_EXHAUSTED\",\n `All ports ${this.config.apiPort}-${lastPort} are occupied. Free a port or stop other Joplin instances.`,\n ),\n )\n }\n this.config.apiPort = resolution.port\n if (resolution.desktopDetected) {\n process.stderr.write(\n \"[joplin-sidecar] WARNING: Joplin Desktop is running. The sidecar uses a separate database.\\n\" +\n \"[joplin-sidecar] Notes will sync between them only if both are configured with the same sync target.\\n\",\n )\n }\n return Right(resolution.port)\n }\n\n isDesktopDetected(): boolean {\n return this.portResolution?.outcome !== \"exhausted\" && (this.portResolution?.desktopDetected ?? false)\n }\n\n async start(): Promise<Either<SidecarError, ChildProcess>> {\n // A shared sidecar can die while we are still running — most often because the\n // process that spawned it exited. Drop a settled start so the next call really\n // restarts instead of replaying an earlier success against a dead server.\n if (this.startPromise && !this.sidecarAlive()) {\n process.stderr.write(\"[joplin-sidecar] Sidecar is no longer running, restarting\\n\")\n this.startPromise = null\n this.childProcess = null\n this.portResolution = null\n }\n if (this.startPromise) return this.startPromise\n this.startPromise = this.doStart()\n return this.startPromise\n }\n\n private async doStart(): Promise<Either<SidecarError, ChildProcess>> {\n // Step 1: Find CLI\n const cliResult = await findJoplinCli()\n if (Either.isLeft(cliResult)) {\n return Left(\n cliResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n const cli = cliResult.fold(\n () => \"\",\n (v) => v,\n )\n process.stderr.write(`[joplin-sidecar] Found CLI: ${cli}\\n`)\n\n // Step 2: Resolve port if not already done (defensive fallback)\n if (!this.portResolution) {\n const portResult = await this.resolvePort()\n if (Either.isLeft(portResult)) {\n return Left(\n portResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n }\n\n // Step 3: Adopt a healthy server serving our profile, so several MCP processes\n // share one sidecar rather than each spawning its own.\n if (this.portResolution?.outcome === \"reuse_existing\") {\n process.stderr.write(\n `[joplin-sidecar] Existing Joplin server for this profile on port ${this.config.apiPort}, reusing\\n`,\n )\n return Right(this.childProcess ?? (null as unknown as ChildProcess))\n }\n\n // Step 3b: A server from a previous release or a changed sync config holds the\n // port. Tear it down first so this version's sidecar replaces it.\n if (this.portResolution?.outcome === \"replace_stale\") {\n await this.replaceStaleServer(this.config.apiPort)\n }\n\n // Step 4: Configure via CLI (skip if config is cached)\n const configHash = computeConfigHash(this.config)\n if (isConfigCached(this.config.profileDir, configHash)) {\n process.stderr.write(\"[joplin-sidecar] Configuration cached, skipping config step\\n\")\n } else {\n const configResult = await configureJoplin(cli, this.config)\n if (Either.isLeft(configResult)) {\n return Left(\n configResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n writeConfigCache(this.config.profileDir, configHash)\n process.stderr.write(\"[joplin-sidecar] Configuration applied via CLI\\n\")\n }\n\n // Step 5: Spawn server (port is free, skip if already spawned from a previous attempt)\n if (!this.childProcess) {\n const spawnResult = spawnServer(cli, this.config)\n if (Either.isLeft(spawnResult)) {\n return Left(\n spawnResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n const proc = spawnResult.fold(\n () => null as unknown as ChildProcess,\n (v) => v,\n )\n this.childProcess = proc\n this.registerCleanup()\n process.stderr.write(`[joplin-sidecar] Server spawned (pid: ${proc.pid})\\n`)\n } else {\n process.stderr.write(\n `[joplin-sidecar] Server already spawned (pid: ${this.childProcess.pid}), waiting for ready\\n`,\n )\n }\n\n // Step 6: Wait for ready\n const readyResult = await waitForReady(this.config.apiPort, this.config.apiToken, this.childProcess)\n if (Either.isLeft(readyResult)) {\n return Left(\n readyResult.fold(\n (e) => e,\n () => null as never,\n ),\n )\n }\n\n // Step 7: Stamp the profile so other processes can tell whether this running\n // server matches their release and sync configuration.\n writeSidecarStamp(this.config.profileDir, {\n version: this.config.version ?? \"unknown\",\n identity: this.identity(),\n })\n\n return Right(this.childProcess!)\n }\n\n async stop(): Promise<Either<Error, true>> {\n // Reset startPromise to allow retry via start() after stop()\n this.startPromise = null\n\n if (!this.childProcess) return Right(true as const)\n\n const proc = this.childProcess\n try {\n proc.kill(\"SIGTERM\")\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n proc.kill(\"SIGKILL\")\n resolve()\n }, 5000)\n\n proc.on(\"exit\", () => {\n clearTimeout(timeout)\n resolve()\n })\n })\n\n this.reapRecordedServer()\n this.childProcess = null\n process.stderr.write(\"[joplin-sidecar] Server stopped\\n\")\n return Right(true as const)\n } catch (error) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async healthCheck(): Promise<Either<Error, true>> {\n try {\n const response = await fetchWithTimeout(`http://127.0.0.1:${this.config.apiPort}/ping`, 5_000)\n if (!response.ok) return Left(new Error(`Health check failed: ${response.status}`))\n return Right(true as const)\n } catch (error) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n getPort(): number {\n return this.config.apiPort\n }\n\n getHost(): string {\n return \"127.0.0.1\"\n }\n}\n","import fs from \"fs\"\nimport { Either, Left, Option, Right } from \"functype\"\nimport { Path, Platform } from \"functype-os\"\nimport { join, resolve } from \"path\"\n\nimport type { SyncTarget } from \"./joplin-sidecar.js\"\n\nexport type ParsedArgs = {\n remainingArgs: string[]\n transport: \"stdio\" | \"http\"\n httpPort: number\n profileDir: string\n syncTarget: Option<SyncTarget>\n // A token passed explicitly via --token. Kept distinct from an ambient\n // JOPLIN_TOKEN so sidecar mode can honor the deliberate flag while ignoring an\n // inherited environment variable that belongs to a different (external) Joplin.\n explicitToken: Option<string>\n}\n\n// Local expandVars preserves the existing \"silently substitute empty for missing\n// env vars\" behavior — functype-os's Path.expandVars returns Left on unresolved\n// variables, which would break paths like ~/${MAYBE_UNSET}/foo.\nconst expandVars = (p: string): string =>\n p\n .replace(/\\$\\{(\\w+)\\}/g, (_, name) => process.env[name] ?? \"\")\n .replace(/\\$(\\w+)/g, (_, name) => process.env[name] ?? \"\")\n\nconst isNonEmptyDir = (p: string): boolean => {\n try {\n const entries = fs.readdirSync(p)\n return entries.length > 0\n } catch {\n return false\n }\n}\n\n// On WSL, a path like ~/OneDrive may resolve to an empty Linux directory while\n// the real data lives under the Windows user profile. Fall back to the WSL\n// user's Windows home (resolved via cmd.exe %USERPROFILE%) when that happens.\nconst resolveWslPath = (linuxPath: string, relativeToHome: string): string => {\n if (!Platform.isWSL()) return linuxPath\n if (fs.existsSync(linuxPath) && isNonEmptyDir(linuxPath)) return linuxPath\n return Platform.windowsHomeDir()\n .map((winHome) => join(winHome, relativeToHome))\n .filter(isNonEmptyDir)\n .map((winPath) => {\n process.stderr.write(`[wsl] Path ${linuxPath} empty/missing, using Windows path: ${winPath}\\n`)\n return winPath\n })\n .orElse(linuxPath)\n}\n\nconst expandPath = (p: string): string => {\n const expanded = expandVars(p)\n const tildeExpanded = Path.expandTilde(expanded)\n if (expanded === \"~\" || expanded.startsWith(\"~/\")) {\n const relativeToHome = expanded === \"~\" ? \"\" : expanded.slice(2)\n return resolveWslPath(tildeExpanded, relativeToHome)\n }\n return tildeExpanded\n}\n\nconst extractArg = (args: string[], flag: string): Option<string> => {\n const index = args.indexOf(flag)\n if (index === -1) return Option.none()\n const value = args[index + 1]\n if (!value || value.startsWith(\"--\")) return Option.none()\n args.splice(index, 2)\n return Option(value)\n}\n\nexport const buildSyncTarget = (args: {\n syncTarget: Option<string>\n syncPath: Option<string>\n syncUsername: Option<string>\n syncPassword: Option<string>\n}): Either<string, SyncTarget> => {\n const targetType = args.syncTarget.orElse(\"none\")\n\n switch (targetType) {\n case \"none\":\n return Right({ type: \"none\" } as SyncTarget)\n\n case \"filesystem\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for filesystem sync target\"),\n (path) => Right({ type: \"filesystem\", path } as SyncTarget),\n )\n\n case \"webdav\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for webdav sync target\"),\n (url) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username required for webdav sync target\"),\n (username) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for webdav sync target\"),\n (password) => Right({ type: \"webdav\", url, username, password } as SyncTarget),\n ),\n ),\n )\n\n case \"nextcloud\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for nextcloud sync target\"),\n (url) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username required for nextcloud sync target\"),\n (username) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for nextcloud sync target\"),\n (password) => Right({ type: \"nextcloud\", url, username, password } as SyncTarget),\n ),\n ),\n )\n\n case \"joplin-cloud\":\n return args.syncUsername.fold(\n () => Left(\"--sync-username required for joplin-cloud sync target\"),\n (email) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for joplin-cloud sync target\"),\n (password) => Right({ type: \"joplin-cloud\", email, password } as SyncTarget),\n ),\n )\n\n case \"joplin-server\":\n return args.syncPath.fold(\n () => Left(\"--sync-path required for joplin-server sync target\"),\n (url) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username required for joplin-server sync target\"),\n (email) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password required for joplin-server sync target\"),\n (password) => Right({ type: \"joplin-server\", url, email, password } as SyncTarget),\n ),\n ),\n )\n\n case \"s3\":\n return args.syncPath.fold(\n () => Left(\"--sync-path (bucket) required for s3 sync target\"),\n (bucket) =>\n args.syncUsername.fold(\n () => Left(\"--sync-username (access key) required for s3 sync target\"),\n (accessKey) =>\n args.syncPassword.fold(\n () => Left(\"--sync-password (secret key) required for s3 sync target\"),\n (secretKey) => Right({ type: \"s3\", bucket, region: \"us-east-1\", accessKey, secretKey } as SyncTarget),\n ),\n ),\n )\n\n case \"dropbox\":\n return Right({ type: \"dropbox\" } as SyncTarget)\n\n case \"onedrive\":\n return Right({ type: \"onedrive\" } as SyncTarget)\n\n default:\n return Left(\n `Unknown sync target: ${targetType}. Valid targets: none, filesystem, webdav, nextcloud, joplin-cloud, joplin-server, s3, dropbox, onedrive`,\n )\n }\n}\n\nfunction parseArgs(): ParsedArgs {\n const args = process.argv.slice(2)\n\n // Load environment variables without dotenv debug output (for MCP stdio compatibility)\n const loadEnvFile = (envPath: string) => {\n try {\n if (fs.existsSync(envPath)) {\n process.stderr.write(`Loading environment from: ${envPath}\\n`)\n const envContent = fs.readFileSync(envPath, \"utf-8\")\n const envLines = envContent.split(\"\\n\")\n const loadedVars: string[] = []\n\n for (const line of envLines) {\n const trimmedLine = line.trim()\n if (trimmedLine && !trimmedLine.startsWith(\"#\")) {\n const [key, ...valueParts] = trimmedLine.split(\"=\")\n if (key && valueParts.length > 0) {\n const value = valueParts.join(\"=\").replace(/^[\"']|[\"']$/g, \"\")\n if (!process.env[key.trim()]) {\n process.env[key.trim()] = value\n loadedVars.push(key.trim())\n }\n }\n }\n }\n\n if (loadedVars.length > 0) {\n process.stderr.write(`Loaded variables: ${loadedVars.join(\", \")}\\n`)\n }\n }\n } catch (error: unknown) {\n process.stderr.write(`Error loading environment file: ${error}\\n`)\n }\n }\n\n // Handle --env-file\n const envFile = extractArg(args, \"--env-file\")\n envFile.fold(\n () => loadEnvFile(\".env\"),\n (file) => loadEnvFile(resolve(process.cwd(), file)),\n )\n\n // Handle --token. Surfaced as an explicit value rather than written into the\n // environment, so downstream can tell a deliberate flag from an inherited var.\n const explicitToken = extractArg(args, \"--token\")\n\n // Handle --transport\n const transport: \"stdio\" | \"http\" = extractArg(args, \"--transport\")\n .map((value): \"stdio\" | \"http\" => {\n if (value !== \"stdio\" && value !== \"http\") {\n process.stderr.write(\"Error: --transport must be either 'stdio' or 'http'\\n\")\n process.exit(1)\n }\n return value\n })\n .orElse(\"stdio\")\n\n // Handle --http-port\n const httpPort: number = extractArg(args, \"--http-port\")\n .map((value) => {\n const parsed = parseInt(value, 10)\n if (isNaN(parsed) || parsed < 1 || parsed > 65535) {\n process.stderr.write(\"Error: --http-port must be a valid port number (1-65535)\\n\")\n process.exit(1)\n }\n return parsed\n })\n .orElse(3000)\n\n // Handle --profile\n const profileDir = extractArg(args, \"--profile\")\n .or(Option(process.env.JOPLIN_PROFILE))\n .map(expandPath)\n .orElse(expandPath(\"~/.config/joplin-mcp\"))\n\n // Handle sync args\n const syncTarget = extractArg(args, \"--sync-target\").or(Option(process.env.JOPLIN_SYNC_TARGET))\n const syncPath = extractArg(args, \"--sync-path\").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath)\n const syncUsername = extractArg(args, \"--sync-username\").or(Option(process.env.JOPLIN_SYNC_USERNAME))\n const syncPassword = extractArg(args, \"--sync-password\").or(Option(process.env.JOPLIN_SYNC_PASSWORD))\n\n // Build and validate sync target\n const syncResult = buildSyncTarget({ syncTarget, syncPath, syncUsername, syncPassword })\n if (Either.isLeft(syncResult)) {\n const err = syncResult.fold(\n (e) => e,\n () => \"\",\n )\n process.stderr.write(`Error: ${err}\\n`)\n process.exit(1)\n }\n const syncTargetValue = syncResult.fold(\n () => ({ type: \"none\" }) as SyncTarget,\n (v) => v,\n )\n const resolvedSyncTarget: Option<SyncTarget> =\n syncTargetValue.type === \"none\" ? Option.none<SyncTarget>() : Option(syncTargetValue as SyncTarget)\n\n // Handle --help\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n process.stderr.write(`\nJoplin MCP Server (Sidecar Mode)\n\nUSAGE:\n joplin-mcp-server [OPTIONS]\n\nOPTIONS:\n --env-file <file> Load environment variables from file\n --token <token> Joplin API token\n --transport <type> Transport type: stdio (default) or http\n --http-port <port> HTTP server port (default: 3000, only with --transport http)\n --profile <dir> Joplin data directory (default: ~/.config/joplin-mcp)\n --sync-target <type> Sync target: none, filesystem, webdav, nextcloud,\n joplin-cloud, joplin-server, s3, dropbox, onedrive\n --sync-path <url> URL or path for sync target\n --sync-username <user> Username/email for sync\n --sync-password <pass> Password for sync\n --help, -h Show this help message\n\nENVIRONMENT VARIABLES:\n JOPLIN_TOKEN API token for external mode (JOPLIN_HOST/JOPLIN_PORT).\n Ignored in sidecar mode, which manages its own token.\n JOPLIN_HOST Connect to existing Joplin at this host (skips sidecar)\n JOPLIN_PORT Connect to existing Joplin on this port (skips sidecar)\n JOPLIN_CLI Path to joplin CLI binary (overrides auto-detection)\n JOPLIN_PROFILE Joplin data directory\n JOPLIN_SYNC_TARGET Sync target type\n JOPLIN_SYNC_PATH Sync target URL/path\n JOPLIN_SYNC_USERNAME Sync username/email\n JOPLIN_SYNC_PASSWORD Sync password\n LOG_LEVEL Log level: debug, info, warn, error (default: info)\n\nMODES:\n Sidecar (default):\n Spawns and manages its own Joplin Terminal process.\n No Joplin desktop app or Web Clipper needed.\n Uses an isolated profile at --profile path (default: ~/.config/joplin-mcp).\n\n External (JOPLIN_HOST/JOPLIN_PORT set):\n Connects directly to an existing Joplin instance.\n Useful for WSL connecting to Windows Joplin desktop.\n\nEXAMPLES:\n # Minimal - local notes, no sync\n joplin-mcp-server --token my_token\n\n # Joplin Cloud sync\n joplin-mcp-server --token my_token \\\\\n --sync-target joplin-cloud \\\\\n --sync-username user@example.com --sync-password pass\n\n # WebDAV sync\n joplin-mcp-server --token my_token \\\\\n --sync-target webdav \\\\\n --sync-path https://dav.example.com/joplin \\\\\n --sync-username user --sync-password pass\n\n # Filesystem sync (Syncthing, NAS)\n joplin-mcp-server --token my_token \\\\\n --sync-target filesystem --sync-path /mnt/sync/joplin\n\n # HTTP transport for web apps\n joplin-mcp-server --token my_token --transport http --http-port 3000\n\n # External mode - connect to existing Joplin (e.g. Windows desktop from WSL)\n JOPLIN_HOST=172.x.x.x JOPLIN_PORT=41184 joplin-mcp-server --token my_token\n\nFind your Joplin token in: Tools > Options > Web Clipper\n`)\n process.exit(0)\n }\n\n return {\n remainingArgs: args,\n transport,\n httpPort,\n profileDir,\n syncTarget: resolvedSyncTarget,\n explicitToken,\n }\n}\n\nexport default parseArgs\nexport { expandPath, expandVars, resolveWslPath }\n","import type { Option } from \"functype\"\n\nexport type TokenSource = \"explicit\" | \"external-env\" | \"external-generated\" | \"profile\"\n\nexport type TokenResolution = {\n // Which channel supplied the token — see TokenSource. Callers use this to know\n // whether to read/persist the profile token and whether to warn.\n source: TokenSource\n // True when an ambient JOPLIN_TOKEN was present but ignored (sidecar mode).\n ambientIgnored: boolean\n}\n\nexport type ResolveTokenInput = {\n externalMode: boolean\n explicitToken: Option<string>\n ambientToken: Option<string>\n}\n\n// Decides where a Joplin token comes from, without performing any I/O.\n//\n// The token means different things per mode. In external mode it is a guest\n// credential for a Joplin we do not own, so a caller must supply it. In sidecar\n// mode we own the instance and mint the token in the profile; an explicit --token\n// still wins, but an ambient JOPLIN_TOKEN is ignored so that several sidecar\n// processes sharing a profile agree on one token without env coordination.\nexport const resolveTokenSource = (input: ResolveTokenInput): TokenResolution => {\n if (input.explicitToken.isEmpty === false) {\n return { source: \"explicit\", ambientIgnored: false }\n }\n if (input.externalMode) {\n return {\n source: input.ambientToken.isEmpty === false ? \"external-env\" : \"external-generated\",\n ambientIgnored: false,\n }\n }\n return { source: \"profile\", ambientIgnored: input.ambientToken.isEmpty === false }\n}\n\n// External mode requires a caller-supplied token; sidecar mode never does.\nexport const externalTokenMissing = (input: ResolveTokenInput): boolean =>\n input.externalMode && input.explicitToken.isEmpty && input.ambientToken.isEmpty\n","import { type Either } from \"functype\"\n\nimport type JoplinAPIClient from \"../joplin-api-client.js\"\n\n/**\n * Thrown by tools to signal an operation failure that should surface to the\n * caller (and the LLM agent) as an MCP error response (isError: true), rather\n * than being silently returned as a \"successful\" text result.\n */\nclass ToolError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"ToolError\"\n }\n}\n\n/**\n * Extract a Joplin-style error message from an API response body. Joplin can\n * return HTTP 200 with `{ error: \"...\" }` (or `{ error: ..., code: ... }`)\n * when an operation fails — without this check the tool would treat the\n * malformed payload as a success.\n */\nconst extractJoplinErrorMessage = (data: unknown): string | undefined => {\n if (data !== null && typeof data === \"object\" && \"error\" in data) {\n const err = (data as { error: unknown }).error\n if (typeof err === \"string\" && err.length > 0) return err\n }\n return undefined\n}\n\ntype JoplinFolder = {\n id: string\n title: string\n parent_id?: string\n}\n\ntype JoplinNote = {\n id: string\n title: string\n body?: string\n parent_id?: string\n created_time: number\n updated_time: number\n is_todo: boolean\n todo_completed?: boolean\n todo_due?: number\n}\n\nabstract class BaseTool {\n protected apiClient: JoplinAPIClient\n\n constructor(apiClient: JoplinAPIClient) {\n this.apiClient = apiClient\n }\n\n abstract call(...args: unknown[]): Promise<string>\n\n protected formatError(error: unknown, context: string): string {\n process.stderr.write(`${context} error: ${error}\\n`)\n const message = error instanceof Error ? error.message : \"Unknown error\"\n return `Error ${context.toLowerCase()}: ${message}`\n }\n\n protected validateId(id: string, type: \"note\" | \"notebook\"): string | null {\n if (!id) {\n return `Please provide a ${type} ID. Example: ${type === \"note\" ? \"read_note\" : \"read_notebook\"} ${type}_id=\"your-${type}-id\"`\n }\n\n if (id.length < 10 || !id.match(/[a-f0-9]/i)) {\n const searchHint = type === \"note\" ? \"search_notes\" : \"list_notebooks\"\n return `Error: \"${id}\" does not appear to be a valid ${type} ID. \\n\\n${type.charAt(0).toUpperCase() + type.slice(1)} IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse ${searchHint} to ${type === \"note\" ? \"find notes\" : \"see all available notebooks\"} and their IDs.`\n }\n\n return null\n }\n\n protected unwrap<T>(result: Either<Error, T>): T {\n return result.fold(\n (err) => {\n throw err\n },\n (val) => val,\n )\n }\n\n protected formatDate(timestamp: number): string {\n return new Date(timestamp).toLocaleString()\n }\n}\n\nexport default BaseTool\nexport { extractJoplinErrorMessage, ToolError }\nexport type { JoplinFolder, JoplinNote }\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface CreateFolderOptions {\n title: string\n parent_id?: string | undefined\n}\n\ninterface CreateFolderResponse extends JoplinFolder {\n created_time: number\n updated_time: number\n}\n\nclass CreateFolder extends BaseTool {\n async call(options: CreateFolderOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide folder creation options. Example: create_folder {\"title\": \"My Notebook\"}'\n }\n\n // Validate required title\n if (!options.title || typeof options.title !== \"string\" || options.title.trim() === \"\") {\n return 'Please provide a title for the folder/notebook. Example: create_folder {\"title\": \"My Notebook\"}'\n }\n\n // Validate parent_id if provided\n if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n }\n\n try {\n // Prepare the request body\n const requestBody: CreateFolderOptions = {\n title: options.title.trim(),\n }\n\n if (options.parent_id) {\n requestBody.parent_id = options.parent_id\n }\n\n // Create the folder\n const createdFolder = this.unwrap(await this.apiClient.post<CreateFolderResponse>(\"/folders\", requestBody))\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails. Catch that before treating it as success.\n const joplinError = extractJoplinErrorMessage(createdFolder)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to create notebook: ${joplinError}`)\n }\n if (!createdFolder || typeof createdFolder !== \"object\" || !createdFolder.id) {\n throw new ToolError(\n `Failed to create notebook: Joplin API returned an unexpected response (no folder id). Raw response: ${JSON.stringify(createdFolder)}`,\n )\n }\n\n // Get parent notebook info if available\n const parentInfo = await (async (): Promise<string> => {\n if (!createdFolder.parent_id) return \"Top level\"\n try {\n const parentNotebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${createdFolder.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (parentNotebook?.title) {\n return `Inside \"${parentNotebook.title}\" (notebook_id: \"${createdFolder.parent_id}\")`\n }\n return `Parent notebook ID: ${createdFolder.parent_id}`\n } catch {\n return `Parent notebook ID: ${createdFolder.parent_id}`\n }\n })()\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully created notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Notebook Details:`)\n resultLines.push(` Title: \"${createdFolder.title}\"`)\n resultLines.push(` Notebook ID: ${createdFolder.id}`)\n resultLines.push(` Location: ${parentInfo}`)\n\n const createdDate = this.formatDate(createdFolder.created_time)\n resultLines.push(` Created: ${createdDate}`)\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${createdFolder.id}\"`)\n resultLines.push(` - Create a note in it: create_note {\"title\": \"My Note\", \"parent_id\": \"${createdFolder.id}\"}`)\n resultLines.push(` - View all notebooks: list_notebooks`)\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number; data?: { error?: string } } }\n process.stderr.write(`creating notebook error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to create notebook: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n if (err.response.status === 404 && options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to create notebook: Parent notebook with ID \"${options.parent_id}\" not found. Use list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`,\n )\n }\n if (err.response.status === 409) {\n throw new ToolError(\n `Failed to create notebook: A notebook with the title \"${options.title}\" might already exist in this location. Try a different title or check existing notebooks with list_notebooks.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to create notebook: ${message}`)\n }\n }\n}\n\nexport default CreateFolder\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface CreateNoteOptions {\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n image_data_url?: string | undefined\n}\n\ntype CreateNoteResponse = JoplinNote\n\nclass CreateNote extends BaseTool {\n async call(options: CreateNoteOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide note creation options. Example: create_note {\"title\": \"My Note\", \"body\": \"Note content\"}'\n }\n\n // Validate that we have at least a title or body\n if (!options.title && !options.body && !options.body_html) {\n return \"Please provide at least a title, body, or body_html for the note.\"\n }\n\n // Validate parent_id if provided\n if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n }\n\n try {\n // Prepare the request body\n const requestBody: CreateNoteOptions = {}\n\n if (options.title) requestBody.title = options.title\n if (options.body) requestBody.body = options.body\n if (options.body_html) requestBody.body_html = options.body_html\n if (options.parent_id) requestBody.parent_id = options.parent_id\n if (options.is_todo !== undefined) requestBody.is_todo = options.is_todo\n if (options.image_data_url) requestBody.image_data_url = options.image_data_url\n\n // Create the note\n const createdNote = this.unwrap(await this.apiClient.post<CreateNoteResponse>(\"/notes\", requestBody))\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails (e.g. invalid parent_id). Catch that before treating\n // it as a successful response.\n const joplinError = extractJoplinErrorMessage(createdNote)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to create note: ${joplinError}`)\n }\n if (!createdNote || typeof createdNote !== \"object\" || !createdNote.id) {\n throw new ToolError(\n `Failed to create note: Joplin API returned an unexpected response (no note id). Raw response: ${JSON.stringify(createdNote)}`,\n )\n }\n\n // Get notebook info if available\n const notebookInfo = await (async (): Promise<string> => {\n if (!createdNote.parent_id) return \"Root level\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${createdNote.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${createdNote.parent_id}\")`\n }\n return `Notebook ID: ${createdNote.parent_id}`\n } catch {\n return `Notebook ID: ${createdNote.parent_id}`\n }\n })()\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully created note!`)\n resultLines.push(\"\")\n resultLines.push(`📝 Note Details:`)\n resultLines.push(` Title: \"${createdNote.title || \"Untitled\"}\"`)\n resultLines.push(` Note ID: ${createdNote.id}`)\n resultLines.push(` Location: ${notebookInfo}`)\n\n if (createdNote.is_todo) {\n resultLines.push(` Type: Todo item`)\n }\n\n const createdDate = this.formatDate(createdNote.created_time)\n resultLines.push(` Created: ${createdDate}`)\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - Read the note: read_note note_id=\"${createdNote.id}\"`)\n if (createdNote.parent_id) {\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${createdNote.parent_id}\"`)\n }\n resultLines.push(` - Search for it: search_notes query=\"${createdNote.title}\"`)\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n // Re-throw ToolErrors as-is (already formatted for the caller)\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number; data?: { error?: string } } }\n process.stderr.write(`creating note error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to create note: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n if (err.response.status === 404 && options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to create note: Notebook with ID \"${options.parent_id}\" not found. Use list_notebooks to see available notebooks and their IDs.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to create note: ${message}`)\n }\n }\n}\n\nexport default CreateNote\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface DeleteFolderOptions {\n folder_id: string\n confirm?: boolean | undefined\n force?: boolean | undefined\n}\n\ntype FolderItem = { id: string; title?: string; parent_id?: string }\n\ninterface FolderContents {\n items: FolderItem[]\n}\n\nclass DeleteFolder extends BaseTool {\n async call(options: DeleteFolderOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n }\n\n // Validate required folder_id\n if (!options.folder_id) {\n return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n }\n\n const folderIdError = this.validateId(options.folder_id, \"notebook\")\n if (folderIdError) {\n return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n }\n\n // Require explicit confirmation for safety\n if (!options.confirm) {\n return `⚠️ This will permanently delete the notebook/folder!\\n\\nTo confirm deletion, use:\\ndelete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true}\\n\\n⚠️ This action cannot be undone!`\n }\n\n try {\n // First, get the folder details to show what's being deleted\n const folderToDelete = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const folderLoadError = extractJoplinErrorMessage(folderToDelete)\n if (folderLoadError !== undefined) {\n throw new ToolError(`Failed to load folder \"${options.folder_id}\": ${folderLoadError}`)\n }\n if (!folderToDelete?.id) {\n throw new ToolError(\n `Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n\n // Check if folder contains notes or subfolders\n const [notes, subfolders] = await Promise.all([\n this.apiClient\n .get<FolderContents>(`/folders/${options.folder_id}/notes`, {\n query: { fields: \"id,title\" },\n })\n .then((result) =>\n result.fold(\n () => ({ items: [] }),\n (data) => data,\n ),\n ),\n this.apiClient\n .get<FolderContents>(\"/folders\", {\n query: { fields: \"id,title,parent_id\" },\n })\n .then((result) =>\n result.fold<FolderContents>(\n () => ({ items: [] }),\n (response) => ({\n items: response.items.filter((folder) => folder.parent_id === options.folder_id),\n }),\n ),\n ),\n ])\n\n const noteCount = notes.items.length\n const subfolderCount = subfolders.items.length\n const totalContent = noteCount + subfolderCount\n\n // Warn if folder is not empty and force is not specified\n if (totalContent > 0 && !options.force) {\n const resultLines: string[] = []\n resultLines.push(`⚠️ Cannot delete non-empty notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Notebook: \"${folderToDelete.title}\"`)\n resultLines.push(` Contains: ${noteCount} notes and ${subfolderCount} subfolders`)\n\n if (noteCount > 0) {\n resultLines.push(\"\")\n resultLines.push(`📝 Contains ${noteCount} notes:`)\n notes.items.slice(0, 5).forEach((note) => {\n resultLines.push(` - ${note.title ?? \"Untitled\"}`)\n })\n if (noteCount > 5) {\n resultLines.push(` ... and ${noteCount - 5} more notes`)\n }\n }\n\n if (subfolderCount > 0) {\n resultLines.push(\"\")\n resultLines.push(`📁 Contains ${subfolderCount} subfolders:`)\n subfolders.items.slice(0, 5).forEach((folder) => {\n resultLines.push(` - ${folder.title ?? \"Untitled\"}`)\n })\n if (subfolderCount > 5) {\n resultLines.push(` ... and ${subfolderCount - 5} more folders`)\n }\n }\n\n resultLines.push(\"\")\n resultLines.push(`💡 Options:`)\n resultLines.push(` 1. Move or delete the contents first, then delete the folder`)\n resultLines.push(` 2. Force delete (⚠️ DESTROYS ALL CONTENT):`)\n resultLines.push(` delete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true, \"force\": true}`)\n resultLines.push(\"\")\n resultLines.push(`⚠️ Force delete will permanently delete ALL ${totalContent} items inside!`)\n\n return resultLines.join(\"\\n\")\n }\n\n // Get parent folder info if available\n const parentInfo = await (async (): Promise<string> => {\n if (!folderToDelete.parent_id) return \"Top level\"\n try {\n const parentFolder = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${folderToDelete.parent_id}`, {\n query: { fields: \"title\" },\n }),\n )\n if (parentFolder?.title) {\n return `Inside \"${parentFolder.title}\" (notebook_id: \"${folderToDelete.parent_id}\")`\n }\n return `Parent ID: ${folderToDelete.parent_id}`\n } catch {\n return `Parent ID: ${folderToDelete.parent_id}`\n }\n })()\n\n // Delete the folder\n const deleteResponse = this.unwrap(await this.apiClient.delete(`/folders/${options.folder_id}`))\n const deleteError = extractJoplinErrorMessage(deleteResponse)\n if (deleteError !== undefined) {\n throw new ToolError(`Failed to delete folder: ${deleteError}`)\n }\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`🗑️ Successfully deleted notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Deleted Notebook Details:`)\n resultLines.push(` Title: \"${folderToDelete.title}\"`)\n resultLines.push(` Folder ID: ${folderToDelete.id}`)\n resultLines.push(` Location: ${parentInfo}`)\n\n if (totalContent > 0) {\n resultLines.push(` Deleted Content: ${noteCount} notes and ${subfolderCount} subfolders`)\n resultLines.push(\"\")\n resultLines.push(`⚠️ All ${totalContent} items inside have been permanently deleted!`)\n }\n\n resultLines.push(\"\")\n resultLines.push(`⚠️ This notebook has been permanently deleted and cannot be recovered.`)\n\n if (folderToDelete.parent_id) {\n resultLines.push(\"\")\n resultLines.push(`🔗 Related actions:`)\n resultLines.push(` - View parent notebook: read_notebook notebook_id=\"${folderToDelete.parent_id}\"`)\n resultLines.push(` - View all notebooks: list_notebooks`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`deleting folder error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404) {\n throw new ToolError(\n `Failed to delete folder: Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n if (err.response.status === 403) {\n throw new ToolError(\n `Failed to delete folder: Permission denied for folder with ID \"${options.folder_id}\". This might be a protected system folder.`,\n )\n }\n if (err.response.status === 409) {\n throw new ToolError(\n `Failed to delete folder: It may contain items that prevent deletion. Try moving or deleting the contents first, or use force option.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to delete folder: ${message}`)\n }\n }\n}\n\nexport default DeleteFolder\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface DeleteNoteOptions {\n note_id: string\n confirm?: boolean | undefined\n}\n\nclass DeleteNote extends BaseTool {\n async call(options: DeleteNoteOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n }\n\n // Validate required note_id\n if (!options.note_id) {\n return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n }\n\n const noteIdError = this.validateId(options.note_id, \"note\")\n if (noteIdError) {\n return noteIdError\n }\n\n // Require explicit confirmation for safety\n if (!options.confirm) {\n return `⚠️ This will permanently delete the note!\\n\\nTo confirm deletion, use:\\ndelete_note {\"note_id\": \"${options.note_id}\", \"confirm\": true}\\n\\n⚠️ This action cannot be undone!`\n }\n\n try {\n // First, get the note details to show what's being deleted\n const noteToDelete = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,created_time,updated_time\" },\n }),\n )\n\n const noteLoadError = extractJoplinErrorMessage(noteToDelete)\n if (noteLoadError !== undefined) {\n throw new ToolError(`Failed to load note \"${options.note_id}\": ${noteLoadError}`)\n }\n if (!noteToDelete?.id) {\n throw new ToolError(\n `Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n\n // Get notebook info if available\n const notebookInfo = await (async (): Promise<string> => {\n if (!noteToDelete.parent_id) return \"Root level\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${noteToDelete.parent_id}`, {\n query: { fields: \"title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${noteToDelete.parent_id}\")`\n }\n return `Notebook ID: ${noteToDelete.parent_id}`\n } catch {\n return `Notebook ID: ${noteToDelete.parent_id}`\n }\n })()\n\n // Delete the note\n const deleteResponse = this.unwrap(await this.apiClient.delete(`/notes/${options.note_id}`))\n const deleteError = extractJoplinErrorMessage(deleteResponse)\n if (deleteError !== undefined) {\n throw new ToolError(`Failed to delete note: ${deleteError}`)\n }\n\n // Format success response\n const resultLines: string[] = []\n resultLines.push(`🗑️ Successfully deleted note!`)\n resultLines.push(\"\")\n resultLines.push(`📝 Deleted Note Details:`)\n resultLines.push(` Title: \"${noteToDelete.title || \"Untitled\"}\"`)\n resultLines.push(` Note ID: ${noteToDelete.id}`)\n resultLines.push(` Location: ${notebookInfo}`)\n\n if (noteToDelete.is_todo) {\n const status = noteToDelete.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(` Type: Todo (${status})`)\n } else {\n resultLines.push(` Type: Regular note`)\n }\n\n const createdDate = this.formatDate(noteToDelete.created_time)\n const updatedDate = this.formatDate(noteToDelete.updated_time)\n resultLines.push(` Created: ${createdDate}`)\n resultLines.push(` Last Updated: ${updatedDate}`)\n\n // Show content preview if available\n if (noteToDelete.body) {\n const preview = noteToDelete.body.substring(0, 100).replace(/\\n/g, \" \")\n const truncated = noteToDelete.body.length > 100 ? \"...\" : \"\"\n resultLines.push(` Content Preview: ${preview}${truncated}`)\n }\n\n resultLines.push(\"\")\n resultLines.push(`⚠️ This note has been permanently deleted and cannot be recovered.`)\n\n if (noteToDelete.parent_id) {\n resultLines.push(\"\")\n resultLines.push(`🔗 Related actions:`)\n resultLines.push(` - View containing notebook: read_notebook notebook_id=\"${noteToDelete.parent_id}\"`)\n resultLines.push(` - Search for similar notes: search_notes query=\"${noteToDelete.title}\"`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`deleting note error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404) {\n throw new ToolError(\n `Failed to delete note: Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n if (err.response.status === 403) {\n throw new ToolError(\n `Failed to delete note: Permission denied for note with ID \"${options.note_id}\". This might be a protected system note.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to delete note: ${message}`)\n }\n }\n}\n\nexport default DeleteNote\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface EditFolderOptions {\n folder_id: string\n title?: string | undefined\n parent_id?: string | undefined\n}\n\ninterface EditFolderResponse extends JoplinFolder {\n updated_time: number\n}\n\nclass EditFolder extends BaseTool {\n async call(options: EditFolderOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n }\n\n // Validate required folder_id\n if (!options.folder_id) {\n return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n }\n\n const folderIdError = this.validateId(options.folder_id, \"notebook\")\n if (folderIdError) {\n return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n }\n\n // Validate that we have at least one field to update\n const updateFields = [\"title\", \"parent_id\"]\n const hasUpdate = updateFields.some((field) => options[field as keyof EditFolderOptions] !== undefined)\n\n if (!hasUpdate) {\n return \"Please provide at least one field to update. Available fields: title, parent_id\"\n }\n\n // Validate title if provided\n if (options.title !== undefined && (typeof options.title !== \"string\" || options.title.trim() === \"\")) {\n return \"Title must be a non-empty string.\"\n }\n\n // Validate parent_id if provided\n if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n }\n\n // Prevent self-parenting\n if (options.parent_id === options.folder_id) {\n return \"Error: A folder cannot be its own parent.\"\n }\n }\n\n try {\n // First, get the current folder to show before/after comparison\n const currentFolder = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const currentFolderError = extractJoplinErrorMessage(currentFolder)\n if (currentFolderError !== undefined) {\n throw new ToolError(`Failed to load folder \"${options.folder_id}\": ${currentFolderError}`)\n }\n if (!currentFolder?.id) {\n throw new ToolError(\n `Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n\n // Prepare the update body - only include fields that are being updated\n const updateBody: Partial<EditFolderOptions> = {}\n\n if (options.title !== undefined) updateBody.title = options.title.trim()\n if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n\n // Update the folder\n const updatedFolder = this.unwrap(\n await this.apiClient.put<EditFolderResponse>(`/folders/${options.folder_id}`, updateBody),\n )\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails. Catch that before treating it as success.\n const joplinError = extractJoplinErrorMessage(updatedFolder)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to update folder: ${joplinError}`)\n }\n if (!updatedFolder || typeof updatedFolder !== \"object\" || !updatedFolder.id) {\n throw new ToolError(\n `Failed to update folder: Joplin API returned an unexpected response (no folder id). Raw response: ${JSON.stringify(updatedFolder)}`,\n )\n }\n\n // Get parent folder info for both old and new locations if parent_id changed\n const fetchParentInfo = async (parentId: string): Promise<string> => {\n try {\n const parent = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${parentId}`, {\n query: { fields: \"title\" },\n }),\n )\n if (parent?.title) {\n return `Inside \"${parent.title}\"`\n }\n return `Parent ID: ${parentId}`\n } catch {\n return `Parent ID: ${parentId}`\n }\n }\n\n const oldParentInfo = currentFolder.parent_id ? await fetchParentInfo(currentFolder.parent_id) : \"Top level\"\n const newParentInfo = await (async (): Promise<string> => {\n if (!updatedFolder.parent_id) return \"Top level\"\n if (updatedFolder.parent_id === currentFolder.parent_id) return oldParentInfo\n return fetchParentInfo(updatedFolder.parent_id)\n })()\n\n // Format success response with before/after comparison\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully updated notebook!`)\n resultLines.push(\"\")\n resultLines.push(`📁 Notebook: \"${updatedFolder.title}\"`)\n resultLines.push(` Folder ID: ${updatedFolder.id}`)\n resultLines.push(\"\")\n\n // Show what changed\n resultLines.push(`🔄 Changes made:`)\n\n if (options.title !== undefined && currentFolder.title !== updatedFolder.title) {\n resultLines.push(` Title: \"${currentFolder.title}\" → \"${updatedFolder.title}\"`)\n }\n\n if (options.parent_id !== undefined && currentFolder.parent_id !== updatedFolder.parent_id) {\n resultLines.push(` Location: ${oldParentInfo} → ${newParentInfo}`)\n }\n\n if (updatedFolder.updated_time) {\n const updatedTime = this.formatDate(updatedFolder.updated_time)\n resultLines.push(` Last Updated: ${updatedTime}`)\n }\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${updatedFolder.id}\"`)\n resultLines.push(` - View all notebooks: list_notebooks`)\n if (updatedFolder.parent_id) {\n resultLines.push(` - View parent notebook: read_notebook notebook_id=\"${updatedFolder.parent_id}\"`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as {\n response?: { status?: number; data?: { error?: string } }\n config?: { url?: string }\n }\n process.stderr.write(`updating folder error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404) {\n if (err.config?.url?.includes(`/folders/${options.folder_id}`) === true) {\n throw new ToolError(\n `Failed to update folder: Folder with ID \"${options.folder_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n if (options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to update folder: Parent folder with ID \"${options.parent_id}\" not found. Use list_notebooks to see available folders and their IDs.`,\n )\n }\n }\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to update folder: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n if (err.response.status === 409) {\n throw new ToolError(\n `Failed to update folder: A folder with the title \"${options.title ?? \"\"}\" might already exist in this location. Try a different title or check existing folders with list_notebooks.`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to update folder: ${message}`)\n }\n }\n}\n\nexport default EditFolder\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface EditNoteOptions {\n note_id: string\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n todo_completed?: boolean | undefined\n todo_due?: number | undefined\n}\n\ntype EditNoteResponse = JoplinNote\n\nclass EditNote extends BaseTool {\n async call(options: EditNoteOptions): Promise<string> {\n if (!options || typeof options !== \"object\") {\n return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n }\n\n // Validate required note_id\n if (!options.note_id) {\n return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n }\n\n const noteIdError = this.validateId(options.note_id, \"note\")\n if (noteIdError) {\n return noteIdError\n }\n\n // Validate that we have at least one field to update\n const updateFields = [\"title\", \"body\", \"body_html\", \"parent_id\", \"is_todo\", \"todo_completed\", \"todo_due\"]\n const hasUpdate = updateFields.some((field) => options[field as keyof EditNoteOptions] !== undefined)\n\n if (!hasUpdate) {\n return \"Please provide at least one field to update. Available fields: title, body, body_html, parent_id, is_todo, todo_completed, todo_due\"\n }\n\n // Validate parent_id if provided\n if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n }\n }\n\n try {\n // First, get the current note to show before/after comparison\n const currentNote = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,todo_due,updated_time\" },\n }),\n )\n\n const currentNoteError = extractJoplinErrorMessage(currentNote)\n if (currentNoteError !== undefined) {\n throw new ToolError(`Failed to load note \"${options.note_id}\": ${currentNoteError}`)\n }\n if (!currentNote?.id) {\n throw new ToolError(\n `Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n\n // Prepare the update body - only include fields that are being updated\n const updateBody: Partial<EditNoteOptions> = {}\n\n if (options.title !== undefined) updateBody.title = options.title\n if (options.body !== undefined) updateBody.body = options.body\n if (options.body_html !== undefined) updateBody.body_html = options.body_html\n if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n if (options.is_todo !== undefined) updateBody.is_todo = options.is_todo\n if (options.todo_completed !== undefined) updateBody.todo_completed = options.todo_completed\n if (options.todo_due !== undefined) updateBody.todo_due = options.todo_due\n\n // Update the note\n const updatedNote = this.unwrap(\n await this.apiClient.put<EditNoteResponse>(`/notes/${options.note_id}`, updateBody),\n )\n\n // Joplin can respond with HTTP 200 and `{ error: \"...\" }` in the body when\n // an operation fails. Catch that before treating it as success.\n const joplinError = extractJoplinErrorMessage(updatedNote)\n if (joplinError !== undefined) {\n throw new ToolError(`Failed to update note: ${joplinError}`)\n }\n if (!updatedNote || typeof updatedNote !== \"object\" || !updatedNote.id) {\n throw new ToolError(\n `Failed to update note: Joplin API returned an unexpected response (no note id). Raw response: ${JSON.stringify(updatedNote)}`,\n )\n }\n\n // Get notebook info for both old and new locations if parent_id changed\n const fetchNotebookInfo = async (parentId: string): Promise<string> => {\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${parentId}`, {\n query: { fields: \"title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\"`\n }\n return `Notebook ID: ${parentId}`\n } catch {\n return `Notebook ID: ${parentId}`\n }\n }\n\n const oldNotebookInfo = currentNote.parent_id ? await fetchNotebookInfo(currentNote.parent_id) : \"Root level\"\n const newNotebookInfo = await (async (): Promise<string> => {\n if (!updatedNote.parent_id) return \"Root level\"\n if (updatedNote.parent_id === currentNote.parent_id) return oldNotebookInfo\n return fetchNotebookInfo(updatedNote.parent_id)\n })()\n\n // Format success response with before/after comparison\n const resultLines: string[] = []\n resultLines.push(`✅ Successfully updated note!`)\n resultLines.push(\"\")\n resultLines.push(`📝 Note: \"${updatedNote.title || \"Untitled\"}\"`)\n resultLines.push(` Note ID: ${updatedNote.id}`)\n resultLines.push(\"\")\n\n // Show what changed\n resultLines.push(`🔄 Changes made:`)\n\n if (options.title !== undefined && currentNote.title !== updatedNote.title) {\n resultLines.push(` Title: \"${currentNote.title}\" → \"${updatedNote.title}\"`)\n }\n\n if (options.parent_id !== undefined && currentNote.parent_id !== updatedNote.parent_id) {\n resultLines.push(` Location: ${oldNotebookInfo} → ${newNotebookInfo}`)\n }\n\n if (options.is_todo !== undefined && currentNote.is_todo !== updatedNote.is_todo) {\n const oldType = currentNote.is_todo ? \"Todo\" : \"Regular note\"\n const newType = updatedNote.is_todo ? \"Todo\" : \"Regular note\"\n resultLines.push(` Type: ${oldType} → ${newType}`)\n }\n\n if (options.todo_completed !== undefined && currentNote.todo_completed !== updatedNote.todo_completed) {\n const oldStatus = currentNote.todo_completed ? \"Completed\" : \"Not completed\"\n const newStatus = updatedNote.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(` Todo Status: ${oldStatus} → ${newStatus}`)\n }\n\n if (options.todo_due !== undefined) {\n const oldDue = currentNote.todo_due ? this.formatDate(currentNote.todo_due) : \"No due date\"\n const newDue = updatedNote.todo_due ? this.formatDate(updatedNote.todo_due) : \"No due date\"\n if (oldDue !== newDue) {\n resultLines.push(` Due Date: ${oldDue} → ${newDue}`)\n }\n }\n\n if (options.body !== undefined) {\n resultLines.push(` Content: Updated`)\n }\n\n if (options.body_html !== undefined) {\n resultLines.push(` HTML Content: Updated`)\n }\n\n const updatedTime = this.formatDate(updatedNote.updated_time)\n resultLines.push(` Last Updated: ${updatedTime}`)\n\n resultLines.push(\"\")\n resultLines.push(`🔗 Next steps:`)\n resultLines.push(` - Read the note: read_note note_id=\"${updatedNote.id}\"`)\n if (updatedNote.parent_id) {\n resultLines.push(` - View notebook: read_notebook notebook_id=\"${updatedNote.parent_id}\"`)\n }\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number; data?: { error?: string } } }\n process.stderr.write(`updating note error: ${error}\\n`)\n if (err.response) {\n if (err.response.status === 404 && options.parent_id !== undefined) {\n throw new ToolError(\n `Failed to update note: Notebook with ID \"${options.parent_id}\" not found. Use list_notebooks to see available notebooks and their IDs.`,\n )\n }\n if (err.response.status === 404) {\n throw new ToolError(\n `Failed to update note: Note with ID \"${options.note_id}\" not found. Use search_notes to find notes and their IDs.`,\n )\n }\n if (err.response.status === 400) {\n throw new ToolError(\n `Failed to update note: Invalid request data. ${err.response.data?.error ?? \"Please check your input parameters.\"}`,\n )\n }\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to update note: ${message}`)\n }\n }\n}\n\nexport default EditNote\n","import type { JoplinFolder } from \"./base-tool.js\"\nimport BaseTool, { ToolError } from \"./base-tool.js\"\n\nclass ListNotebooks extends BaseTool {\n async call(): Promise<string> {\n try {\n const notebooks = this.unwrap(\n await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const notebooksByParentId: Record<string, JoplinFolder[]> = {}\n\n notebooks.forEach((notebook) => {\n const parentId = notebook.parent_id ?? \"\"\n if (!notebooksByParentId[parentId]) {\n notebooksByParentId[parentId] = []\n }\n notebooksByParentId[parentId].push(notebook)\n })\n\n // Add a header with instructions\n const resultLines = [\n \"Joplin Notebooks:\\n\",\n \"NOTE: To read a notebook, use the notebook_id with the read_notebook command\\n\",\n 'Example: read_notebook notebook_id=\"your-notebook-id\"\\n\\n',\n ]\n\n // Add the notebook hierarchy\n resultLines.push(\n ...this.notebooksLines(notebooksByParentId[\"\"] ?? [], {\n indent: 0,\n notebooksByParentId,\n }),\n )\n\n return resultLines.join(\"\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n process.stderr.write(`listing notebooks error: ${error}\\n`)\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to list notebooks: ${message}`)\n }\n }\n\n private notebooksLines(\n notebooks: JoplinFolder[],\n {\n indent = 0,\n notebooksByParentId,\n }: {\n indent: number\n notebooksByParentId: Record<string, JoplinFolder[]>\n },\n ): string[] {\n const result: string[] = []\n const indentSpaces = \" \".repeat(indent)\n\n this.sortNotebooks(notebooks).forEach((notebook) => {\n const { id } = notebook\n result.push(`${indentSpaces}Notebook: \"${notebook.title}\" (notebook_id: \"${id}\")\\n`)\n\n const childNotebooks = notebooksByParentId[id]\n if (childNotebooks) {\n result.push(\n ...this.notebooksLines(childNotebooks, {\n indent: indent + 2,\n notebooksByParentId,\n }),\n )\n }\n })\n\n return result\n }\n\n private sortNotebooks(notebooks: JoplinFolder[]): JoplinFolder[] {\n // Ensure that notebooks starting with '[0]' are sorted first\n const CHARACTER_BEFORE_A = String.fromCharCode(\"A\".charCodeAt(0) - 1)\n return [...notebooks].sort((a, b) => {\n const titleA = a.title.replace(\"[\", CHARACTER_BEFORE_A)\n const titleB = b.title.replace(\"[\", CHARACTER_BEFORE_A)\n return titleA.localeCompare(titleB)\n })\n }\n}\n\nexport default ListNotebooks\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool from \"./base-tool.js\"\n\nclass ReadMultiNote extends BaseTool {\n async call(noteIds: string[]): Promise<string> {\n if (!noteIds || !Array.isArray(noteIds) || noteIds.length === 0) {\n return 'Please provide an array of note IDs. Example: read_multinote note_ids=[\"id1\", \"id2\", \"id3\"]'\n }\n\n // Validate that all IDs look like valid note IDs\n const invalidIds = noteIds.filter((id) => !id || id.length < 10 || !id.match(/[a-f0-9]/i))\n if (invalidIds.length > 0) {\n return `Error: Some IDs do not appear to be valid note IDs: ${invalidIds.join(\", \")}\\n\\nNote IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse search_notes to find notes and their IDs.`\n }\n\n const resultLines: string[] = []\n const notFound: string[] = []\n const errors: string[] = []\n const successful: string[] = []\n\n // Add a header\n resultLines.push(`# Reading ${noteIds.length} notes\\n`)\n\n // Process each note ID\n for (const [i, noteId] of noteIds.entries()) {\n resultLines.push(`## Note ${i + 1} of ${noteIds.length} (ID: ${noteId})\\n`)\n\n try {\n // Get the note details with all relevant fields\n const note = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n query: {\n fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n },\n }),\n )\n\n // Validate note response\n if (!note || typeof note !== \"object\" || !note.id) {\n errors.push(noteId)\n resultLines.push(`Error: Unexpected response format from Joplin API when fetching note ${noteId}\\n`)\n continue\n }\n\n successful.push(noteId)\n\n // Get the notebook info to show where this note is located\n const notebookInfo = await (async (): Promise<string> => {\n if (!note.parent_id) return \"Unknown notebook\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n }\n return \"Unknown notebook\"\n } catch (err: unknown) {\n process.stderr.write(`Error fetching notebook info for note ${noteId}: ${err}\\n`)\n return \"Unknown notebook\"\n }\n })()\n\n // Add note metadata\n resultLines.push(`### Note: \"${note.title}\"`)\n resultLines.push(`Notebook: ${notebookInfo}`)\n\n // Add todo status if applicable\n if (note.is_todo) {\n const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(`Status: ${status}`)\n\n if (note.todo_due) {\n const dueDate = this.formatDate(note.todo_due)\n resultLines.push(`Due: ${dueDate}`)\n }\n }\n\n // Add timestamps\n const createdDate = this.formatDate(note.created_time)\n const updatedDate = this.formatDate(note.updated_time)\n resultLines.push(`Created: ${createdDate}`)\n resultLines.push(`Updated: ${updatedDate}`)\n\n // Add a separator before the note content\n resultLines.push(\"\\n---\\n\")\n\n // Add the note body\n if (note.body) {\n resultLines.push(note.body)\n } else {\n resultLines.push(\"(This note has no content)\")\n }\n\n // Add a separator after the note\n resultLines.push(\"\\n---\\n\")\n } catch (error: unknown) {\n process.stderr.write(`Error reading note ${noteId}: ${error}\\n`)\n const err = error as { response?: { status?: number }; message?: string }\n if (err.response?.status === 404) {\n notFound.push(noteId)\n resultLines.push(`Note with ID \"${noteId}\" not found.\\n`)\n } else {\n errors.push(noteId)\n resultLines.push(`Error reading note: ${err.message ?? \"Unknown error\"}\\n`)\n }\n }\n }\n\n // Add a summary at the end\n resultLines.push(\"# Summary\")\n resultLines.push(`Total notes requested: ${noteIds.length}`)\n resultLines.push(`Successfully retrieved: ${successful.length}`)\n\n if (notFound.length > 0) {\n resultLines.push(`Notes not found: ${notFound.length}`)\n resultLines.push(`IDs not found: ${notFound.join(\", \")}`)\n }\n\n if (errors.length > 0) {\n resultLines.push(`Errors encountered: ${errors.length}`)\n resultLines.push(`IDs with errors: ${errors.join(\", \")}`)\n }\n\n return resultLines.join(\"\\n\")\n }\n}\n\nexport default ReadMultiNote\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\nclass ReadNote extends BaseTool {\n async call(noteId: string): Promise<string> {\n const validationError = this.validateId(noteId, \"note\")\n if (validationError) {\n return validationError\n }\n\n try {\n // Get the note details with all relevant fields\n const note = this.unwrap(\n await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n query: {\n fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n },\n }),\n )\n\n const noteLoadError = extractJoplinErrorMessage(note)\n if (noteLoadError !== undefined) {\n throw new ToolError(`Failed to read note \"${noteId}\": ${noteLoadError}`)\n }\n if (!note || typeof note !== \"object\" || !note.id) {\n throw new ToolError(\n `Failed to read note \"${noteId}\": Joplin API returned an unexpected response (no note id). Raw response: ${JSON.stringify(note)}`,\n )\n }\n\n // Get the notebook info to show where this note is located\n const notebookInfo = await (async (): Promise<string> => {\n if (!note.parent_id) return \"Unknown notebook\"\n try {\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n query: { fields: \"id,title\" },\n }),\n )\n if (notebook?.title) {\n return `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n }\n return \"Unknown notebook\"\n } catch (err: unknown) {\n process.stderr.write(`Error fetching notebook info: ${err}\\n`)\n return \"Unknown notebook\"\n }\n })()\n\n // Format the note content\n const resultLines: string[] = []\n\n // Add note header with metadata\n resultLines.push(`# Note: \"${note.title}\"`)\n resultLines.push(`Note ID: ${note.id}`)\n resultLines.push(`Notebook: ${notebookInfo}`)\n\n // Add todo status if applicable\n if (note.is_todo) {\n const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n resultLines.push(`Status: ${status}`)\n\n if (note.todo_due) {\n const dueDate = this.formatDate(note.todo_due)\n resultLines.push(`Due: ${dueDate}`)\n }\n }\n\n // Add timestamps\n const createdDate = this.formatDate(note.created_time)\n const updatedDate = this.formatDate(note.updated_time)\n resultLines.push(`Created: ${createdDate}`)\n resultLines.push(`Updated: ${updatedDate}`)\n\n // Add a separator before the note content\n resultLines.push(\"\\n---\\n\")\n\n // Add the note body\n if (note.body) {\n resultLines.push(note.body)\n } else {\n resultLines.push(\"(This note has no content)\")\n }\n\n // Add a footer with helpful commands\n resultLines.push(\"\\n---\\n\")\n resultLines.push(\"Related commands:\")\n resultLines.push(`- To view the notebook containing this note: read_notebook notebook_id=\"${note.parent_id}\"`)\n resultLines.push('- To search for more notes: search_notes query=\"your search term\"')\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`reading note error: ${error}\\n`)\n if (err.response?.status === 404) {\n throw new ToolError(\n `Note with ID \"${noteId}\" not found. This might happen if the ID is incorrect, you're using a notebook ID instead of a note ID, or the note has been deleted. Use search_notes to find notes and their IDs.`,\n )\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(\n `Failed to read note \"${noteId}\": ${message}. Make sure you're using a valid note ID. Use search_notes to find notes and their IDs.`,\n )\n }\n }\n}\n\nexport default ReadNote\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { extractJoplinErrorMessage, ToolError } from \"./base-tool.js\"\n\ninterface NotebookNotesResponse {\n items: JoplinNote[]\n}\n\nclass ReadNotebook extends BaseTool {\n async call(notebookId: string): Promise<string> {\n const validationError = this.validateId(notebookId, \"notebook\")\n if (validationError) {\n return validationError\n }\n\n try {\n // First, get the notebook details\n const notebook = this.unwrap(\n await this.apiClient.get<JoplinFolder>(`/folders/${notebookId}`, {\n query: { fields: \"id,title,parent_id\" },\n }),\n )\n\n const notebookLoadError = extractJoplinErrorMessage(notebook)\n if (notebookLoadError !== undefined) {\n throw new ToolError(`Failed to read notebook \"${notebookId}\": ${notebookLoadError}`)\n }\n if (!notebook || typeof notebook !== \"object\" || !notebook.id) {\n throw new ToolError(\n `Failed to read notebook \"${notebookId}\": Joplin API returned an unexpected response (no notebook id). Raw response: ${JSON.stringify(notebook)}`,\n )\n }\n\n // Get all notes in this notebook\n const notes = this.unwrap(\n await this.apiClient.get<NotebookNotesResponse>(`/folders/${notebookId}/notes`, {\n query: { fields: \"id,title,updated_time,is_todo,todo_completed\" },\n }),\n )\n\n const notesLoadError = extractJoplinErrorMessage(notes)\n if (notesLoadError !== undefined) {\n throw new ToolError(`Failed to load notes for notebook \"${notebookId}\": ${notesLoadError}`)\n }\n if (!notes || typeof notes !== \"object\") {\n throw new ToolError(\n `Failed to load notes for notebook \"${notebookId}\": Joplin API returned an unexpected response. Raw response: ${JSON.stringify(notes)}`,\n )\n }\n\n if (!notes.items || !Array.isArray(notes.items) || notes.items.length === 0) {\n return `Notebook \"${notebook.title}\" (notebook_id: \"${notebook.id}\") is empty.\\n\\nTry another notebook ID or use list_notebooks to see all available notebooks.`\n }\n\n // Format the notebook contents\n const resultLines: string[] = []\n resultLines.push(`# Notebook: \"${notebook.title}\" (notebook_id: \"${notebook.id}\")`)\n resultLines.push(`Contains ${notes.items.length} notes:\\n`)\n resultLines.push(`NOTE: This is showing the contents of notebook \"${notebook.title}\", not a specific note.\\n`)\n\n // If multiple notes were found, add a hint about read_multinote\n if (notes.items.length > 1) {\n const noteIds = notes.items.map((note) => note.id)\n resultLines.push(`TIP: To read all ${notes.items.length} notes at once, use:\\n`)\n resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n }\n\n // Sort notes by updated_time (newest first)\n const sortedNotes = [...notes.items].sort((a, b) => b.updated_time - a.updated_time)\n\n sortedNotes.forEach((note) => {\n const updatedDate = this.formatDate(note.updated_time)\n\n // Add checkbox for todos\n if (note.is_todo) {\n const checkboxStatus = note.todo_completed ? \"✅\" : \"☐\"\n resultLines.push(`- ${checkboxStatus} Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n } else {\n resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n }\n\n resultLines.push(` Updated: ${updatedDate}`)\n resultLines.push(` To read this note: read_note note_id=\"${note.id}\"`)\n resultLines.push(\"\") // Empty line between notes\n })\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n\n const err = error as { response?: { status?: number } }\n process.stderr.write(`reading notebook error: ${error}\\n`)\n if (err.response?.status === 404) {\n throw new ToolError(\n `Notebook with ID \"${notebookId}\" not found. This might happen if the ID is incorrect, you're using a note title instead of a notebook ID, or the notebook has been deleted. Use list_notebooks to see all available notebooks with their IDs.`,\n )\n }\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(\n `Failed to read notebook \"${notebookId}\": ${message}. Make sure you're using a valid notebook ID, not a note title. Use list_notebooks to see all available notebooks with their IDs.`,\n )\n }\n }\n}\n\nexport default ReadNotebook\n","import type { JoplinFolder, JoplinNote } from \"./base-tool.js\"\nimport BaseTool, { ToolError } from \"./base-tool.js\"\n\ninterface SearchResult {\n items: JoplinNote[]\n}\n\nclass SearchNotes extends BaseTool {\n async call(query: string): Promise<string> {\n if (!query) {\n return \"Please provide a search query.\"\n }\n\n try {\n // Search for notes with the given query\n const searchResults = this.unwrap(\n await this.apiClient.get<SearchResult>(\"/search\", {\n query: {\n query,\n fields: \"id,title,body,parent_id,updated_time\",\n },\n }),\n )\n\n // Handle case where the API doesn't return the expected structure\n if (!searchResults || typeof searchResults !== \"object\") {\n return `Error: Unexpected response format from Joplin API`\n }\n\n // Handle case where no items were found\n if (!searchResults.items || !Array.isArray(searchResults.items) || searchResults.items.length === 0) {\n return `No notes found matching query: \"${query}\"`\n }\n\n // Get all folders to be able to show notebook names\n const folders = this.unwrap(\n await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n query: {\n fields: \"id,title\",\n },\n }),\n )\n\n // Create a map of folder IDs to folder titles for quick lookup\n const folderMap: Record<string, string> = {}\n folders.forEach((folder) => {\n folderMap[folder.id] = folder.title\n })\n\n // Format the search results\n const resultLines: string[] = []\n resultLines.push(`Found ${searchResults.items.length} notes matching query: \"${query}\"\\n`)\n resultLines.push(`NOTE: To read a notebook, use the notebook ID (not the note title)\\n`)\n\n // If multiple notes were found, add a hint about read_multinote\n if (searchResults.items.length > 1) {\n const noteIds = searchResults.items.map((note) => note.id)\n resultLines.push(`TIP: To read all ${searchResults.items.length} notes at once, use:\\n`)\n resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n }\n\n searchResults.items.forEach((note) => {\n const notebookTitle = folderMap[note.parent_id ?? \"\"] ?? \"Unknown notebook\"\n const notebookId = note.parent_id ?? \"unknown\"\n const updatedDate = this.formatDate(note.updated_time)\n\n resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n resultLines.push(` Notebook: \"${notebookTitle}\" (notebook_id: \"${notebookId}\")`)\n resultLines.push(` Updated: ${updatedDate}`)\n\n // Add a snippet of the note body if available\n if (note.body) {\n const snippet = note.body.substring(0, 100).replace(/\\n/g, \" \") + (note.body.length > 100 ? \"...\" : \"\")\n resultLines.push(` Snippet: ${snippet}`)\n }\n\n // Add hints for using related commands\n resultLines.push(` To read this note: read_note note_id=\"${note.id}\"`)\n resultLines.push(` To read this notebook: read_notebook notebook_id=\"${notebookId}\"`)\n\n resultLines.push(\"\") // Empty line between notes\n })\n\n return resultLines.join(\"\\n\")\n } catch (error: unknown) {\n if (error instanceof ToolError) throw error\n process.stderr.write(`searching notes error: ${error}\\n`)\n const message = error instanceof Error ? error.message : \"Unknown error\"\n throw new ToolError(`Failed to search notes: ${message}`)\n }\n }\n}\n\nexport default SearchNotes\n","import axios, { type AxiosResponse } from \"axios\"\nimport type { Either } from \"functype\"\nimport { Left, Right } from \"functype\"\n\ntype JoplinAPIClientConfig = {\n host?: string\n port?: number\n token: string\n}\n\ntype JoplinAPIResponse<T = unknown> = {\n items: T[]\n has_more: boolean\n}\n\ntype RequestOptions = {\n query?: Record<string, unknown>\n [key: string]: unknown\n}\n\nclass JoplinAPIClient {\n private readonly baseURL: string\n private readonly token: string\n\n constructor({ host = \"127.0.0.1\", port = 41184, token }: JoplinAPIClientConfig) {\n this.baseURL = `http://${host}:${port}`\n this.token = token\n }\n\n async serviceAvailable(): Promise<Either<Error, true>> {\n try {\n const response: AxiosResponse<string> = await axios.get(`${this.baseURL}/ping`, { timeout: 5_000 })\n if (response.status === 200 && response.data === \"JoplinClipperServer\") {\n return Right(true as const)\n }\n return Left(new Error(\"Unexpected response from Joplin ping\"))\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async getAllItems<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T[]>> {\n const fetchPage = async (page: number, acc: T[]): Promise<T[]> => {\n const result = await this.get<JoplinAPIResponse<T>>(path, this.mergeRequestOptions(options, { query: { page } }))\n const response = result.fold(\n (err) => {\n throw err\n },\n (data) => data,\n )\n if (!Array.isArray(response.items)) {\n throw new Error(`Unexpected response format from Joplin API for path: ${path}`)\n }\n const combined = [...acc, ...response.items]\n return response.has_more ? fetchPage(page + 1, combined) : combined\n }\n\n try {\n return Right(await fetchPage(1, []))\n } catch (error: unknown) {\n process.stderr.write(`Error in getAllItems for path ${path}: ${error}\\n`)\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.get(`${this.baseURL}${path}`, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async post<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.post(`${this.baseURL}${path}`, body, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async delete<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.delete(`${this.baseURL}${path}`, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n async put<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n try {\n const { data }: AxiosResponse<T> = await axios.put(`${this.baseURL}${path}`, body, {\n params: this.requestOptions(options).query,\n timeout: 30_000,\n })\n return Right(data)\n } catch (error: unknown) {\n return Left(error instanceof Error ? error : new Error(String(error)))\n }\n }\n\n private requestOptions(options: RequestOptions = {}): RequestOptions {\n return this.mergeRequestOptions(\n {\n query: { token: this.token },\n },\n options,\n )\n }\n\n private mergeRequestOptions(options1: RequestOptions, options2: RequestOptions): RequestOptions {\n return {\n query: {\n ...(options1.query ?? {}),\n ...(options2.query ?? {}),\n },\n ...this.except(options1, \"query\"),\n ...this.except(options2, \"query\"),\n }\n }\n\n private except(obj: Record<string, unknown>, key: string): Record<string, unknown> {\n const result = { ...obj }\n delete result[key]\n return result\n }\n}\n\nexport default JoplinAPIClient\nexport type { JoplinAPIClientConfig, JoplinAPIResponse, RequestOptions }\n","import { Either } from \"functype\"\n\nimport JoplinAPIClient from \"./lib/joplin-api-client.js\"\nimport type { JoplinSidecar } from \"./lib/joplin-sidecar.js\"\nimport {\n CreateFolder,\n CreateNote,\n DeleteFolder,\n DeleteNote,\n EditFolder,\n EditNote,\n ListNotebooks,\n ReadMultiNote,\n ReadNote,\n ReadNotebook,\n SearchNotes,\n} from \"./lib/tools/index.js\"\n\nexport type JoplinServerConfig = {\n host: string\n port: number\n token: string\n sidecar?: JoplinSidecar\n}\n\nexport class JoplinServerManager {\n private apiClient: JoplinAPIClient\n private config: JoplinServerConfig\n private connected: boolean = false\n private tools: {\n listNotebooks: ListNotebooks\n searchNotes: SearchNotes\n readNotebook: ReadNotebook\n readNote: ReadNote\n readMultiNote: ReadMultiNote\n createNote: CreateNote\n createFolder: CreateFolder\n editNote: EditNote\n editFolder: EditFolder\n deleteNote: DeleteNote\n deleteFolder: DeleteFolder\n }\n\n constructor(config: JoplinServerConfig) {\n this.config = config\n this.apiClient = new JoplinAPIClient({\n host: config.host,\n port: config.port,\n token: config.token,\n })\n\n this.tools = {\n listNotebooks: new ListNotebooks(this.apiClient),\n searchNotes: new SearchNotes(this.apiClient),\n readNotebook: new ReadNotebook(this.apiClient),\n readNote: new ReadNote(this.apiClient),\n readMultiNote: new ReadMultiNote(this.apiClient),\n createNote: new CreateNote(this.apiClient),\n createFolder: new CreateFolder(this.apiClient),\n editNote: new EditNote(this.apiClient),\n editFolder: new EditFolder(this.apiClient),\n deleteNote: new DeleteNote(this.apiClient),\n deleteFolder: new DeleteFolder(this.apiClient),\n }\n }\n\n async ensureConnected(): Promise<void> {\n if (this.connected) {\n const result = await this.apiClient.serviceAvailable()\n if (Either.isRight(result)) return\n this.connected = false\n }\n\n const available = await this.apiClient.serviceAvailable()\n if (Either.isRight(available)) {\n this.connected = true\n process.stderr.write(`Connected to Joplin at ${this.config.host}:${this.config.port}\\n`)\n return\n }\n\n // If sidecar exists, try starting it\n if (this.config.sidecar) {\n process.stderr.write(\"Joplin not available, starting sidecar...\\n\")\n const startResult = await this.config.sidecar.start()\n if (Either.isLeft(startResult)) {\n const error = startResult.fold(\n (e) => e,\n () => null as never,\n )\n throw new Error(`Sidecar failed [${error.code}]: ${error.message}`)\n }\n const retryAvailable = await this.apiClient.serviceAvailable()\n if (Either.isRight(retryAvailable)) {\n this.connected = true\n process.stderr.write(`Connected to Joplin via sidecar at ${this.config.host}:${this.config.port}\\n`)\n return\n }\n }\n\n throw new Error(\n `Joplin is not available at ${this.config.host}:${this.config.port}. ` +\n `Please ensure Joplin is running or configure a sidecar.`,\n )\n }\n\n // Tool execution methods\n async listNotebooks(): Promise<string> {\n await this.ensureConnected()\n return await this.tools.listNotebooks.call()\n }\n\n async searchNotes(query: string): Promise<string> {\n await this.ensureConnected()\n return await this.tools.searchNotes.call(query)\n }\n\n async readNotebook(notebookId: string): Promise<string> {\n await this.ensureConnected()\n return await this.tools.readNotebook.call(notebookId)\n }\n\n async readNote(noteId: string): Promise<string> {\n await this.ensureConnected()\n return await this.tools.readNote.call(noteId)\n }\n\n async readMultiNote(noteIds: string[]): Promise<string> {\n await this.ensureConnected()\n return await this.tools.readMultiNote.call(noteIds)\n }\n\n async createNote(params: {\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n image_data_url?: string | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.createNote.call(params)\n }\n\n async createFolder(params: { title: string; parent_id?: string | undefined }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.createFolder.call(params)\n }\n\n async editNote(params: {\n note_id: string\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n todo_completed?: boolean | undefined\n todo_due?: number | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.editNote.call(params)\n }\n\n async editFolder(params: {\n folder_id: string\n title?: string | undefined\n parent_id?: string | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.editFolder.call(params)\n }\n\n async deleteNote(params: { note_id: string; confirm?: boolean | undefined }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.deleteNote.call(params)\n }\n\n async deleteFolder(params: {\n folder_id: string\n confirm?: boolean | undefined\n force?: boolean | undefined\n }): Promise<string> {\n await this.ensureConnected()\n return await this.tools.deleteFolder.call(params)\n }\n\n async sync(): Promise<string> {\n await this.ensureConnected()\n const desktopWarning = this.config.sidecar?.isDesktopDetected()\n ? \"\\n\\nNote: Joplin Desktop is also running. The sidecar and Desktop use separate databases. \" +\n \"Notes sync between them only if both are configured with the same sync target.\"\n : \"\"\n // Try triggering sync via the Joplin REST API (POST /services/sync)\n const result = await this.apiClient.post<Record<string, unknown>>(\"/services/sync\", { action: \"start\" })\n return result.fold(\n (error) => {\n const msg = error.message || String(error)\n // 404 or \"No action API\" = Joplin instance doesn't expose sync as a REST service\n // This is normal for Joplin Terminal CLI — it auto-syncs on its configured interval\n if (msg.includes(\"404\") || msg.includes(\"No action API\") || msg.includes(\"No such service\")) {\n return (\n `Sync is managed automatically by the Joplin server on its configured interval ` +\n `(default: every 5 minutes). On-demand sync is not available via the Joplin Terminal API.${desktopWarning}`\n )\n }\n return `Sync failed: ${msg}${desktopWarning}`\n },\n () => `Sync triggered successfully.${desktopWarning}`,\n )\n }\n}\n\nexport function initializeJoplinManager(config: JoplinServerConfig): JoplinServerManager {\n return new JoplinServerManager(config)\n}\n","import { FastMCP, UserError } from \"fastmcp\"\nimport { readFileSync } from \"fs\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { z } from \"zod\"\n\nimport { ToolError } from \"./lib/tools/index.js\"\nimport type { JoplinServerManager } from \"./server-core.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\n\n/**\n * Convert a ToolError thrown by a tool into a FastMCP UserError so the error\n * message reaches the agent verbatim with isError: true (instead of being\n * wrapped in FastMCP's generic \"Tool 'x' execution failed: ...\" envelope).\n */\nconst runWithUserError = async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn()\n } catch (e) {\n if (e instanceof ToolError) throw new UserError(e.message)\n throw e\n }\n}\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"))\nconst VERSION = packageJson.version\n\nexport type FastMCPServerOptions = {\n host: string\n port: number\n token: string\n httpPort?: number\n endpoint?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): { server: FastMCP; manager: JoplinServerManager } {\n process.stderr.write(\"Initializing FastMCP server for Joplin...\\n\")\n\n // Initialize Joplin manager\n const manager = initializeJoplinManager({ host: options.host, port: options.port, token: options.token })\n\n // Create FastMCP server\n const server = new FastMCP({\n name: \"joplin\",\n version: VERSION,\n health: {\n enabled: true,\n path: \"/health\",\n status: 200,\n message: JSON.stringify({\n status: \"healthy\",\n service: \"joplin-mcp-server\",\n version: VERSION,\n joplinPort: options.port,\n timestamp: new Date().toISOString(),\n }),\n },\n })\n\n // Add list_notebooks tool\n server.addTool({\n name: \"list_notebooks\",\n description: \"Retrieve the complete notebook hierarchy from Joplin\",\n parameters: z.object({}),\n execute: () => runWithUserError(() => manager.listNotebooks()),\n })\n\n // Add search_notes tool\n server.addTool({\n name: \"search_notes\",\n description: \"Search for notes in Joplin and return matching notebooks\",\n parameters: z.object({\n query: z.string().describe(\"Search query for notes\"),\n }),\n execute: (args) => runWithUserError(() => manager.searchNotes(args.query)),\n })\n\n // Add read_notebook tool\n server.addTool({\n name: \"read_notebook\",\n description: \"Read the contents of a specific notebook\",\n parameters: z.object({\n notebook_id: z.string().describe(\"ID of the notebook to read\"),\n }),\n execute: (args) => runWithUserError(() => manager.readNotebook(args.notebook_id)),\n })\n\n // Add read_note tool\n server.addTool({\n name: \"read_note\",\n description: \"Read the full content of a specific note\",\n parameters: z.object({\n note_id: z.string().describe(\"ID of the note to read\"),\n }),\n execute: (args) => runWithUserError(() => manager.readNote(args.note_id)),\n })\n\n // Add read_multinote tool\n server.addTool({\n name: \"read_multinote\",\n description: \"Read the full content of multiple notes at once\",\n parameters: z.object({\n note_ids: z.array(z.string()).describe(\"Array of note IDs to read\"),\n }),\n execute: (args) => runWithUserError(() => manager.readMultiNote(args.note_ids)),\n })\n\n // Add create_note tool\n server.addTool({\n name: \"create_note\",\n description: \"Create a new note in Joplin\",\n parameters: z.object({\n title: z.string().optional().describe(\"Note title\"),\n body: z.string().optional().describe(\"Note content in Markdown\"),\n body_html: z.string().optional().describe(\"Note content in HTML\"),\n parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n image_data_url: z.string().optional().describe(\"Base64 encoded image data URL\"),\n }),\n execute: (args) => runWithUserError(() => manager.createNote(args)),\n })\n\n // Add create_folder tool\n server.addTool({\n name: \"create_folder\",\n description: \"Create a new folder/notebook in Joplin\",\n parameters: z.object({\n title: z.string().describe(\"Notebook title\"),\n parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n }),\n execute: (args) => runWithUserError(() => manager.createFolder(args)),\n })\n\n // Add edit_note tool\n server.addTool({\n name: \"edit_note\",\n description: \"Edit/update an existing note in Joplin\",\n parameters: z.object({\n note_id: z.string().describe(\"ID of the note to edit\"),\n title: z.string().optional().describe(\"New note title\"),\n body: z.string().optional().describe(\"New note content in Markdown\"),\n body_html: z.string().optional().describe(\"New note content in HTML\"),\n parent_id: z.string().optional().describe(\"New parent notebook ID\"),\n is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n todo_completed: z.boolean().optional().describe(\"Whether todo is completed\"),\n todo_due: z.number().optional().describe(\"Todo due date (Unix timestamp)\"),\n }),\n execute: (args) => runWithUserError(() => manager.editNote(args)),\n })\n\n // Add edit_folder tool\n server.addTool({\n name: \"edit_folder\",\n description: \"Edit/update an existing folder/notebook in Joplin\",\n parameters: z.object({\n folder_id: z.string().describe(\"ID of the folder to edit\"),\n title: z.string().optional().describe(\"New folder title\"),\n parent_id: z.string().optional().describe(\"New parent folder ID\"),\n }),\n execute: (args) => runWithUserError(() => manager.editFolder(args)),\n })\n\n // Add delete_note tool\n server.addTool({\n name: \"delete_note\",\n description: \"Delete a note from Joplin (requires confirmation)\",\n parameters: z.object({\n note_id: z.string().describe(\"ID of the note to delete\"),\n confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n }),\n execute: (args) => runWithUserError(() => manager.deleteNote(args)),\n })\n\n // Add delete_folder tool\n server.addTool({\n name: \"delete_folder\",\n description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n parameters: z.object({\n folder_id: z.string().describe(\"ID of the folder to delete\"),\n confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n force: z.boolean().optional().describe(\"Force delete even if folder has contents\"),\n }),\n execute: (args) => runWithUserError(() => manager.deleteFolder(args)),\n })\n\n // Add sync tool\n server.addTool({\n name: \"sync\",\n description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n parameters: z.object({}),\n execute: () => runWithUserError(() => manager.sync()),\n })\n\n process.stderr.write(\"FastMCP server configured with 12 Joplin tools\\n\")\n return { server, manager }\n}\n\nexport async function startFastMCPServer(options: FastMCPServerOptions): Promise<void> {\n const { server } = createFastMCPServer(options)\n\n process.stderr.write(`Configured for Joplin at ${options.host}:${options.port}\\n`)\n\n const port = options.httpPort ?? 3000\n const endpoint = options.endpoint ?? \"/mcp\"\n\n await server.start({\n transportType: \"httpStream\",\n httpStream: {\n port,\n endpoint: endpoint as `/${string}`,\n },\n })\n\n process.stderr.write(`FastMCP server running on http://0.0.0.0:${port}${endpoint}\\n`)\n}\n","#!/usr/bin/env node\n\ndeclare const __VERSION__: string\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { type CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\"\nimport fs from \"fs\"\nimport { Option } from \"functype\"\nimport path from \"path\"\nimport { fileURLToPath } from \"url\"\n\nimport { DEFAULT_API_PORT, JoplinSidecar, type SyncTarget } from \"./lib/joplin-sidecar.js\"\nimport parseArgs from \"./lib/parse-args.js\"\nimport { externalTokenMissing, resolveTokenSource } from \"./lib/resolve-token.js\"\nimport { ToolError } from \"./lib/tools/index.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\nimport { startFastMCPServer } from \"./server-fastmcp.js\"\n\n// Parse command line arguments\nconst parsedArgs = parseArgs()\nconst { transport, httpPort, profileDir, syncTarget, explicitToken } = parsedArgs\n\nconst isHttpMode = transport === \"http\"\n\n// External mode: JOPLIN_HOST/JOPLIN_PORT set = connect directly, skip sidecar.\n// Normalize empty strings to undefined so `JOPLIN_HOST=` (a common way to\n// \"unset\" an inherited env var) does not trigger external mode with an empty host.\nconst externalHost =\n process.env.JOPLIN_HOST !== undefined && process.env.JOPLIN_HOST !== \"\" ? process.env.JOPLIN_HOST : undefined\nconst externalPort =\n process.env.JOPLIN_PORT !== undefined && process.env.JOPLIN_PORT !== \"\"\n ? parseInt(process.env.JOPLIN_PORT, 10)\n : undefined\nconst externalMode = externalHost !== undefined || externalPort !== undefined\n\nconst generateToken = (): string => `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n\nconst explicitTokenValue = explicitToken.orUndefined()\nconst ambientToken = process.env.JOPLIN_TOKEN\nconst tokenInput = { externalMode, explicitToken, ambientToken: Option(ambientToken) }\n\n// External mode connects to a Joplin we do not own, so a caller-supplied token is\n// required: an explicit --token first, otherwise the ambient JOPLIN_TOKEN.\nif (externalTokenMissing(tokenInput)) {\n process.stderr.write(\n \"Error: a token is required in external mode. Use --token <token> or set JOPLIN_TOKEN environment variable.\\n\",\n )\n process.exit(1)\n}\n\n// Read the sidecar's own token from its profile, generating and persisting one on\n// first run. Persisting keeps the config hash stable across restarts (so the\n// `joplin config` step can be skipped) and lets other MCP processes on the same\n// profile converge on one shared sidecar.\nconst readOrCreateProfileToken = (): string => {\n const tokenPath = path.join(profileDir, \".mcp-token\")\n try {\n const saved = fs.readFileSync(tokenPath, \"utf-8\").trim()\n if (saved) return saved\n } catch {\n // No saved token yet\n }\n const token = generateToken()\n try {\n fs.mkdirSync(profileDir, { recursive: true })\n fs.writeFileSync(tokenPath, token)\n } catch {\n // Non-critical — token still works, just won't be cached\n }\n return token\n}\n\n// Token ownership differs by mode (see resolveTokenSource). In sidecar mode an\n// ambient JOPLIN_TOKEN belongs to a different Joplin and is deliberately ignored.\nconst tokenResolution = resolveTokenSource(tokenInput)\nif (tokenResolution.ambientIgnored) {\n process.stderr.write(\n \"Note: ignoring JOPLIN_TOKEN in sidecar mode; the sidecar manages its own token in the profile. \" +\n \"Pass --token to set one explicitly, or JOPLIN_HOST/JOPLIN_PORT to connect to an external Joplin.\\n\",\n )\n}\nconst joplinToken = ((): string => {\n switch (tokenResolution.source) {\n case \"explicit\":\n return explicitTokenValue ?? generateToken()\n case \"external-env\":\n return ambientToken ?? generateToken()\n case \"external-generated\":\n return generateToken()\n case \"profile\":\n return readOrCreateProfileToken()\n }\n})()\n\nasync function setupConnection(): Promise<{ host: string; port: number; sidecar: JoplinSidecar | undefined }> {\n if (externalMode) {\n // External mode — connect to existing Joplin instance (e.g. Windows desktop from WSL)\n const host = externalHost ?? \"127.0.0.1\"\n const port = externalPort ?? DEFAULT_API_PORT\n process.stderr.write(`External mode: connecting to Joplin at ${host}:${port}\\n`)\n return { host, port, sidecar: undefined }\n }\n\n // Sidecar mode — spawn and manage Joplin Terminal\n const sidecar = new JoplinSidecar({\n profileDir,\n apiPort: DEFAULT_API_PORT,\n apiToken: joplinToken,\n syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n version: __VERSION__,\n })\n\n // Phase 1: Resolve port (fast — a few HTTP probes).\n // Must complete before getPort() so downstream gets the correct port.\n const portResult = await sidecar.resolvePort()\n portResult.fold(\n (err) => process.stderr.write(`Warning: Port resolution failed: ${err.message}\\n`),\n (p) => process.stderr.write(`Sidecar will use port ${p}\\n`),\n )\n\n // Phase 2: Fire-and-forget the slow startup (CLI config, spawn, wait).\n // ensureConnected() in server-core.ts will await or retry on first tool call.\n void sidecar.start().then((result) => {\n result.fold(\n (err) => {\n process.stderr.write(`Warning: Sidecar failed to start: ${err.message}\\n`)\n process.stderr.write(\"Attempting to connect to existing Joplin instance...\\n\")\n },\n () => {\n process.stderr.write(\"Joplin sidecar started successfully\\n\")\n },\n )\n })\n\n // Cleanup on exit\n const cleanup = async () => {\n await sidecar.stop()\n process.exit(0)\n }\n process.on(\"SIGINT\", () => void cleanup())\n process.on(\"SIGTERM\", () => void cleanup())\n\n return { host: sidecar.getHost(), port: sidecar.getPort(), sidecar }\n}\n\n// Main startup logic\nasync function main(): Promise<void> {\n const { host, port, sidecar } = await setupConnection()\n\n if (isHttpMode) {\n process.stderr.write(\"Starting HTTP transport mode with FastMCP...\\n\")\n await startFastMCPServer({\n host,\n port,\n token: joplinToken,\n httpPort,\n endpoint: \"/mcp\",\n })\n } else {\n process.stderr.write(\"Starting stdio transport mode...\\n\")\n await startStdioServer(host, port, joplinToken, sidecar)\n }\n}\n\nmain().catch((error) => {\n process.stderr.write(`Failed to start MCP server: ${error}\\n`)\n process.exit(1)\n})\n\nasync function startStdioServer(host: string, port: number, token: string, sidecar?: JoplinSidecar): Promise<void> {\n const manager = initializeJoplinManager({ host, port, token, sidecar })\n\n const server = new Server(\n {\n name: \"joplin-mcp-server\",\n version: __VERSION__,\n },\n {\n capabilities: {\n resources: {},\n tools: {},\n prompts: {},\n },\n },\n )\n\n // Register tool list handler\n server.setRequestHandler(ListToolsRequestSchema, () => {\n return {\n tools: [\n {\n name: \"list_notebooks\",\n description: \"Retrieve the complete notebook hierarchy from Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n },\n {\n name: \"search_notes\",\n description: \"Search for notes in Joplin and return matching notebooks\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"read_notebook\",\n description: \"Read the contents of a specific notebook\",\n inputSchema: {\n type: \"object\",\n properties: {\n notebook_id: { type: \"string\", description: \"ID of the notebook to read\" },\n },\n required: [\"notebook_id\"],\n },\n },\n {\n name: \"read_note\",\n description: \"Read the full content of a specific note\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_id: { type: \"string\", description: \"ID of the note to read\" },\n },\n required: [\"note_id\"],\n },\n },\n {\n name: \"read_multinote\",\n description: \"Read the full content of multiple notes at once\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_ids: { type: \"array\", items: { type: \"string\" }, description: \"Array of note IDs to read\" },\n },\n required: [\"note_ids\"],\n },\n },\n {\n name: \"create_note\",\n description: \"Create a new note in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Note title\" },\n body: { type: \"string\", description: \"Note content in Markdown\" },\n body_html: { type: \"string\", description: \"Note content in HTML\" },\n parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n image_data_url: { type: \"string\", description: \"Base64 encoded image data URL\" },\n },\n },\n },\n {\n name: \"create_folder\",\n description: \"Create a new folder/notebook in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Notebook title\" },\n parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"edit_note\",\n description: \"Edit/update an existing note in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_id: { type: \"string\", description: \"ID of the note to edit\" },\n title: { type: \"string\", description: \"New note title\" },\n body: { type: \"string\", description: \"New note content in Markdown\" },\n body_html: { type: \"string\", description: \"New note content in HTML\" },\n parent_id: { type: \"string\", description: \"New parent notebook ID\" },\n is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n todo_completed: { type: \"boolean\", description: \"Whether todo is completed\" },\n todo_due: { type: \"number\", description: \"Todo due date (Unix timestamp)\" },\n },\n required: [\"note_id\"],\n },\n },\n {\n name: \"edit_folder\",\n description: \"Edit/update an existing folder/notebook in Joplin\",\n inputSchema: {\n type: \"object\",\n properties: {\n folder_id: { type: \"string\", description: \"ID of the folder to edit\" },\n title: { type: \"string\", description: \"New folder title\" },\n parent_id: { type: \"string\", description: \"New parent folder ID\" },\n },\n required: [\"folder_id\"],\n },\n },\n {\n name: \"delete_note\",\n description: \"Delete a note from Joplin (requires confirmation)\",\n inputSchema: {\n type: \"object\",\n properties: {\n note_id: { type: \"string\", description: \"ID of the note to delete\" },\n confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n },\n required: [\"note_id\"],\n },\n },\n {\n name: \"delete_folder\",\n description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n inputSchema: {\n type: \"object\",\n properties: {\n folder_id: { type: \"string\", description: \"ID of the folder to delete\" },\n confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n force: { type: \"boolean\", description: \"Force delete even if folder has contents\" },\n },\n required: [\"folder_id\"],\n },\n },\n {\n name: \"sync\",\n description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n inputSchema: {\n type: \"object\",\n properties: {},\n },\n },\n ],\n }\n })\n\n // Register tool call handler\n server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const toolName = request.params.name\n const args = request.params.arguments ?? {}\n\n try {\n switch (toolName) {\n case \"list_notebooks\": {\n const listResult = await manager.listNotebooks()\n return { content: [{ type: \"text\", text: listResult }], isError: false }\n }\n\n case \"search_notes\": {\n const searchResult = await manager.searchNotes(args.query as string)\n return { content: [{ type: \"text\", text: searchResult }], isError: false }\n }\n\n case \"read_notebook\": {\n const notebookResult = await manager.readNotebook(args.notebook_id as string)\n return { content: [{ type: \"text\", text: notebookResult }], isError: false }\n }\n\n case \"read_note\": {\n const noteResult = await manager.readNote(args.note_id as string)\n return { content: [{ type: \"text\", text: noteResult }], isError: false }\n }\n\n case \"read_multinote\": {\n const multiResult = await manager.readMultiNote(args.note_ids as string[])\n return { content: [{ type: \"text\", text: multiResult }], isError: false }\n }\n\n case \"create_note\": {\n const createNoteResult = await manager.createNote(\n args as {\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n image_data_url?: string | undefined\n },\n )\n return { content: [{ type: \"text\", text: createNoteResult }], isError: false }\n }\n\n case \"create_folder\": {\n const createFolderResult = await manager.createFolder(\n args as {\n title: string\n parent_id?: string | undefined\n },\n )\n return { content: [{ type: \"text\", text: createFolderResult }], isError: false }\n }\n\n case \"edit_note\": {\n const editNoteResult = await manager.editNote(\n args as {\n note_id: string\n title?: string | undefined\n body?: string | undefined\n body_html?: string | undefined\n parent_id?: string | undefined\n is_todo?: boolean | undefined\n todo_completed?: boolean | undefined\n todo_due?: number | undefined\n },\n )\n return { content: [{ type: \"text\", text: editNoteResult }], isError: false }\n }\n\n case \"edit_folder\": {\n const editFolderResult = await manager.editFolder(\n args as {\n folder_id: string\n title?: string | undefined\n parent_id?: string | undefined\n },\n )\n return { content: [{ type: \"text\", text: editFolderResult }], isError: false }\n }\n\n case \"delete_note\": {\n const deleteNoteResult = await manager.deleteNote(\n args as {\n note_id: string\n confirm?: boolean | undefined\n },\n )\n return { content: [{ type: \"text\", text: deleteNoteResult }], isError: false }\n }\n\n case \"delete_folder\": {\n const deleteFolderResult = await manager.deleteFolder(\n args as {\n folder_id: string\n confirm?: boolean | undefined\n force?: boolean | undefined\n },\n )\n return { content: [{ type: \"text\", text: deleteFolderResult }], isError: false }\n }\n\n case \"sync\": {\n const syncResult = await manager.sync()\n return { content: [{ type: \"text\", text: syncResult }], isError: false }\n }\n\n default:\n throw new Error(`Unknown tool: ${toolName}`)\n }\n } catch (error) {\n // ToolError already carries a user-facing message; pass it through verbatim.\n // For unexpected errors, prefix with \"Error:\" to make the failure obvious.\n const errorMessage = error instanceof Error ? error.message : String(error)\n const text = error instanceof ToolError ? errorMessage : `Error: ${errorMessage}`\n return {\n content: [{ type: \"text\", text }],\n isError: true,\n }\n }\n })\n\n // Create logs directory if it doesn't exist\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = path.dirname(__filename)\n const logsDir = path.join(__dirname, \"..\", \"logs\")\n\n if (!fs.existsSync(logsDir)) {\n fs.mkdirSync(logsDir, { recursive: true })\n }\n\n // Create a log file for this session\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n const logFile = path.join(logsDir, `mcp-server-${timestamp}.log`)\n\n // Create a custom transport wrapper to log commands and responses\n class LoggingTransport extends StdioServerTransport {\n private commandCounter: number\n\n constructor() {\n super()\n this.commandCounter = 0\n }\n\n async sendMessage(message: unknown): Promise<void> {\n const logEntry = {\n timestamp: new Date().toISOString(),\n direction: \"RESPONSE\",\n message,\n }\n\n fs.appendFileSync(logFile, `${JSON.stringify(logEntry)}\\n`)\n\n const parent = Object.getPrototypeOf(Object.getPrototypeOf(this)) as {\n sendMessage: (m: unknown) => Promise<void>\n }\n await parent.sendMessage.call(this, message)\n }\n\n async handleMessage(message: unknown): Promise<void> {\n this.commandCounter++\n const logEntry = {\n timestamp: new Date().toISOString(),\n direction: \"COMMAND\",\n commandNumber: this.commandCounter,\n message,\n }\n\n fs.appendFileSync(logFile, `${JSON.stringify(logEntry)}\\n`)\n\n const parent = Object.getPrototypeOf(Object.getPrototypeOf(this)) as {\n handleMessage: (m: unknown) => Promise<void>\n }\n await parent.handleMessage.call(this, message)\n }\n }\n\n const stdioTransport = new LoggingTransport()\n\n try {\n await server.connect(stdioTransport)\n process.stderr.write(\"MCP server started and ready to receive commands\\n\")\n } catch (error: unknown) {\n process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AASA,MAAM,YAAY,UAAU,IAAI;AAChC,MAAM,gBAAgB,UAAU,QAAQ;AAExC,MAAM,YAAY,SAAS,UAAU;AACrC,MAAM,WAAW,YAAY,UAAU;AAEvC,MAAa,mBAAmB;AAChC,MAAM,oBAAoB;AAuC1B,MAAM,gBAAgB,MAA4B,SAAiB,WAAmC;CACpG;CACA;CACA;AACF;AAEA,MAAM,gBAAgB,WACpB,MAAM,OAAO,IAAI,CAAC,CACf,KAAK,cAAc,CAAC,CAAC,CACrB,KAAK,oBAAoB,CAAC,CAAC,CAC3B,KAAK,gBAAgB,CAAC,CAAC,CACvB,KAAK,mBAAmB,CAAC,CAAC,CAC1B,KAAK,iBAAiB,CAAC,CAAC,CACxB,KAAK,kBAAkB,CAAC,CAAC,CACzB,KAAK,YAAY,CAAC,CAAC,CACnB,KAAK,uBAAuB,CAAC,CAAC,CAC9B,KAAK,sBAAsB,EAAE,CAAC,CAC9B,cAAc,CAAC;AAEpB,MAAM,mBAAmB,OAAO,KAAa,YAAoB,QAA6B;CAC5F,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,QAAQ,iBAAiB,WAAW,MAAM,GAAG,SAAS;CAC5D,IAAI;EACF,OAAO,MAAM,MAAM,KAAK,EAAE,QAAQ,WAAW,OAAO,CAAC;CACvD,UAAU;EACR,aAAa,KAAK;CACpB;AACF;AAWA,MAAM,mBAAmB;AAEzB,MAAM,wBAAwB,eAAuC;CACnE,IAAI;EACF,MAAM,MAAM,GAAG,aAAa,KAAK,YAAY,gBAAgB,GAAG,OAAO,CAAC,CAAC,KAAK;EAC9E,MAAM,MAAM,OAAO,SAAS,KAAK,EAAE;EACnC,OAAO,OAAO,UAAU,GAAG,KAAK,MAAM,IAAI,OAAO,GAAG,IAAI,OAAO,KAAK;CACtE,QAAQ;EACN,OAAO,OAAO,KAAK;CACrB;AACF;AAEA,MAAM,kBAAkB,QAAyB;CAC/C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAKA,MAAM,oBAAoB,eACxB,qBAAqB,UAAU,CAAC,CAAC,WACzB,QACL,QAAQ,eAAe,GAAG,CAC7B;AAEF,MAAM,qBAAqB;AAc3B,MAAM,uBAAuB,WAC3BA,SACG,WAAW,QAAQ,CAAC,CACpB,OACC,KAAK,UAAU;CACb,SAAS,OAAO,WAAW;CAC3B,YAAY,OAAO;CACnB,cAAc,OAAO;AACvB,CAAC,CACH,CAAC,CACA,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;AAEhB,MAAM,oBAAoB,eAA6C;CACrE,IAAI;EAEF,MAAM,QADe,KAAK,MAAM,GAAG,aAAa,KAAK,YAAY,kBAAkB,GAAG,OAAO,CAC7E;EAChB,IAAI,OAAO,MAAM,YAAY,YAAY,OAAO,MAAM,aAAa,UAAU,OAAO,OAAO,KAAK;EAChG,OAAO,OAAO;GAAE,SAAS,MAAM;GAAS,UAAU,MAAM;EAAS,CAAC;CACpE,QAAQ;EACN,OAAO,OAAO,KAAK;CACrB;AACF;AAEA,MAAM,qBAAqB,YAAoB,UAA8B;CAC3E,IAAI;EACF,GAAG,cAAc,KAAK,YAAY,kBAAkB,GAAG,KAAK,UAAU,KAAK,CAAC;CAC9E,QAAQ,CAER;AACF;AAMA,MAAM,yBAAyB,YAAoB,aAAmC;CACpF,IAAI,CAAC,iBAAiB,UAAU,GAAG,OAAO;CAC1C,OAAO,iBAAiB,UAAU,CAAC,CAAC,WAC5B,UACL,UAAW,MAAM,aAAa,WAAW,aAAa,OACzD;AACF;AAEA,MAAM,uBAAuB,QAA0B;;CACrD,MAAM,IAAI;CACV,MAAA,WAAI,EAAE,WAAA,QAAA,aAAA,KAAA,IAAA,KAAA,IAAA,SAAO,UAAS,gBAAgB,OAAO;CAC7C,MAAA,YAAI,EAAE,WAAA,QAAA,cAAA,KAAA,MAAA,YAAA,UAAO,YAAA,QAAA,cAAA,KAAA,IAAA,KAAA,IAAA,UAAQ,MAAM,UAAU,MAAM,SAAS,cAAc,OAAM,MAAM,OAAO;CACrF,OAAO;AACT;AAEA,MAAM,YAAY,OAChB,MACA,OACA,YACA,aAC6B;CAC7B,IAAI;EAGF,IAAI,OADe,MADQ,iBAAiB,oBAAoB,KAAK,QAAQ,GAAK,EAAA,CAClD,KAAK,MACxB,uBAAuB,OAAO;EAG3C,IAAI;GAQF,KAAI,MAPuB,iBACzB,oBAAoB,KAAK,iBAAiB,mBAAmB,KAAK,EAAE,WACpE,GACF,EAAA,CAIiB,IACf,OAAO,MAAM,sBAAsB,YAAY,QAAQ,CAAC,CAAC,CACtD,KAAK,kBAAkB,aAAsB,CAAC,CAC9C,KAAK,eAAe,cAAuB,CAAC,CAC5C,cAAc,gBAAyB;GAE5C,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF,SAAS,KAAc;EAErB,IAAI,oBAAoB,GAAG,GAAG,OAAO;EAErC,OAAO;CACT;AACF;AAQA,MAAM,uBAAuB,OAC3B,WACA,OACA,YACA,aAC4B;CAC5B,MAAM,UAAU,OAAO,MAAc,oBAAsD;EACzF,IAAI,QAAQ,YAAY,mBAAmB,OAAO,EAAE,SAAS,YAAY;EACzE,MAAM,SAAS,MAAM,UAAU,MAAM,OAAO,YAAY,QAAQ;EAChE,IAAI,WAAW,QAAQ,OAAO;GAAE,SAAS;GAAQ;GAAM;EAAgB;EACvE,IAAI,WAAW,eAAe,OAAO;GAAE,SAAS;GAAkB;GAAM;EAAgB;EACxF,IAAI,WAAW,gBAAgB,OAAO;GAAE,SAAS;GAAiB;GAAM;EAAgB;EACxF,IAAI,WAAW,kBAAkB;GAC/B,QAAQ,OAAO,MACb,yBAAyB,KAAK,oHAEhC;GACA,OAAO,QAAQ,OAAO,GAAG,eAAe;EAC1C;EACA,IAAI,WAAW,kBAAkB;GAC/B,QAAQ,OAAO,MACb,yBAAyB,KAAK,uFAEhC;GACA,OAAO,QAAQ,OAAO,GAAG,IAAI;EAC/B;EACA,QAAQ,OAAO,MAAM,yBAAyB,KAAK,aAAa,OAAO,oBAAoB;EAC3F,OAAO,QAAQ,OAAO,GAAG,eAAe;CAC1C;CACA,OAAO,QAAQ,WAAW,KAAK;AACjC;AAEA,MAAM,eAAe,YAAY,eAAe;AAChD,MAAM,mBAAmB;AAMzB,MAAM,gBAAgB,KAAa,cAAgC;CACjE,MAAM,SAAS,QAAQ,GAAG;CAC1B,IAAI,aAAa,KAAK,WAAW,KAAK,OAAO,CAAC,GAAG;CACjD,OAAO,CAAC,KAAK,GAAG,aAAa,QAAQ,YAAY,CAAC,CAAC;AACrD;AAEA,MAAM,2BACJ,CAAC,GAAG,aAAa,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC,GAAG,gBAAgB,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,KAAK,SAC/F,KAAK,MAAM,gBAAgB,QAAQ,YAAY,CACjD;AAEF,MAAM,gBAAgB,YAAmD;CAEvE,MAAM,SAAS,QAAQ,IAAI;CAC3B,IAAI,QAAQ;EACV,IAAI,GAAG,WAAW,MAAM,GAAG,OAAO,MAAM,MAAM;EAC9C,OAAO,KAAK,aAAa,iBAAiB,8BAA8B,QAAQ,CAAC;CACnF;CAGA,MAAM,UAAU,mBAAmB,CAAC,CAAC,MAAM,cAAc,GAAG,WAAW,SAAS,CAAC;CACjF,IAAI,SAAS,OAAO,MAAM,OAAO;CAGjC,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,UAAU,GAAG,SAAS,UAAU;GAAE,UAAU;GAAS,SAAS;EAAO,CAAC;EAC/F,MAAM,aAAa,OAAO,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;EAC7C,OAAO,MAAM,UAAU;CACzB,QAAQ,CAER;CAGA,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,UAAU,GAAG,SAAS,OAAO;GAAE,UAAU;GAAS,SAAS;EAAO,CAAC;EAC5F,MAAM,UAAU,OAAO,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;EAC1C,QAAQ,OAAO,MAAM,iFAAiF;EACtG,OAAO,MAAM,OAAO;CACtB,QAAQ,CAER;CAEA,OAAO,KACL,aACE,iBACA,8FACF,CACF;AACF;AAEA,MAAM,uBAAuB,WAAkD;CAC7E,MAAM,WAAmC;EACvC,aAAa,OAAO;EACpB,YAAY,OAAO,OAAO,OAAO;CACnC;CAEA,MAAM,aAAa,OAAO,OAAO,UAAU,CAAC,CAAC,OAAO,EAAE,MAAM,OAAO,CAAe;CAClF,SAAS,iBAAiB,OAAO,aAAa,UAAU,CAAC;CAEzD,IAAI,WAAW,SAAS,cACtB,SAAS,iBAAiB,WAAW;MAChC,IAAI,WAAW,SAAS,UAAU;EACvC,SAAS,iBAAiB,WAAW;EACrC,SAAS,qBAAqB,WAAW;EACzC,SAAS,qBAAqB,WAAW;CAC3C,OAAO,IAAI,WAAW,SAAS,aAAa;EAC1C,SAAS,iBAAiB,WAAW;EACrC,SAAS,qBAAqB,WAAW;EACzC,SAAS,qBAAqB,WAAW;CAC3C,OAAO,IAAI,WAAW,SAAS,MAAM;EACnC,SAAS,iBAAiB,WAAW;EACrC,SAAS,mBAAmB,WAAW;EACvC,SAAS,qBAAqB,WAAW;EACzC,SAAS,qBAAqB,WAAW;CAC3C,OAAO,IAAI,WAAW,SAAS,iBAAiB;EAC9C,SAAS,iBAAiB,WAAW;EACrC,SAAS,qBAAqB,WAAW;EACzC,SAAS,qBAAqB,WAAW;CAC3C,OAAO,IAAI,WAAW,SAAS,gBAAgB;EAC7C,SAAS,sBAAsB,WAAW;EAC1C,SAAS,sBAAsB,WAAW;CAC5C;CAEA,MAAM,WAAW,OAAO,OAAO,YAAY,CAAC,CAAC,OAAO,GAAG;CACvD,SAAS,mBAAmB,OAAO,QAAQ;CAE3C,OAAO;AACT;AAEA,MAAM,kBAAkB,OAAO,KAAa,YAAoB,KAAa,UAAiC;CAC5G,MAAM,MAAM,IAAI,SAAS,KAAK,IAAI,QAAQ;CAG1C,MAAM,OAAO,IAAI,SAAS,KAAK,IAC3B;EAAC;EAAU;EAAa;EAAY;EAAU;EAAK;CAAK,IACxD;EAAC;EAAa;EAAY;EAAU;EAAK;CAAK;CAClD,MAAM,cAAc,KAAK,MAAM;EAAE,UAAU;EAAS,SAAS;EAAQ,OAAO;CAAU,CAAC;AACzF;AAEA,MAAM,kBAAkB,OAAO,KAAa,WAA+D;CACzG,MAAM,WAAW,oBAAoB,MAAM;CAC3C,IAAI;EACF,GAAG,UAAU,OAAO,YAAY,EAAE,WAAW,KAAK,CAAC;EACnD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,MAAM,gBAAgB,KAAK,OAAO,YAAY,KAAK,KAAK;EAE1D,OAAO,MAAM,KAAA,CAAiB;CAChC,SAAS,GAAG;EACV,OAAO,KAAK,aAAa,iBAAiB,sCAAsC,CAAC,CAAC;CACpF;AACF;AAEA,MAAM,eAAe,KAAa,WAA8D;CAC9F,IAAI;EAOF,MAAM,OAAO,MAND,IAAI,SAAS,KAAK,IAAI,QAAQ,KAE7B,IAAI,SAAS,KAAK,IAC3B;GAAC;GAAU;GAAa,OAAO;GAAY;GAAU;EAAO,IAC5D;GAAC;GAAa,OAAO;GAAY;GAAU;EAAO,GAExB;GAC5B,OAAO;IAAC;IAAU;IAAQ;GAAM;GAChC,UAAU;GACV,OAAO;EACT,CAAC;EAED,KAAK,OAAO,GAAG,SAAS,SAAiB;GACvC,QAAQ,OAAO,MAAM,oBAAoB,KAAK,SAAS,GAAG;EAC5D,CAAC;EAED,KAAK,OAAO,GAAG,SAAS,SAAiB;GACvC,QAAQ,OAAO,MAAM,oBAAoB,KAAK,SAAS,GAAG;EAC5D,CAAC;EAED,KAAK,GAAG,UAAU,QAAQ;GACxB,QAAQ,OAAO,MAAM,mCAAmC,IAAI,QAAQ,GAAG;EACzE,CAAC;EAED,OAAO,MAAM,IAAoB;CACnC,SAAS,GAAG;EACV,OAAO,KAAK,aAAa,gBAAgB,yCAAyC,CAAC,CAAC;CACtF;AACF;AAEA,MAAM,eAAe,OACnB,MACA,OACA,MACA,aAAqB,IACrB,aAAqB,QACmB;CACxC,MAAM,WAAW,KAAK,IAAI,IAAI;CAK9B,MAAM,gBAAyC,EAAE,MAAM,KAAK;CAC5D,IAAI,MACF,KAAK,KAAK,SAAS,SAAS;EAC1B,cAAc,OAAO;CACvB,CAAC;CAGH,MAAM,UAAU,OAAO,MAAmD;EACxE,IAAI,KAAK,cAAc,KAAK,IAAI,IAAI,UAClC,OAAO,KAAK,aAAa,uBAAuB,+CAA+C,CAAC;EAGlG,IAAI,cAAc,SAAS,MACzB,OAAO,KACL,aACE,gBACA,mDAAmD,cAAc,KAAK,UAC5D,KAAK,8DACjB,CACF;EAGF,IAAI;GAEF,KAAI,MADuB,iBAAiB,oBAAoB,KAAK,MAAM,EAAA,CAC1D,IACf,IAAI;IACF,MAAM,eAAe,MAAM,iBACzB,oBAAoB,KAAK,iBAAiB,mBAAmB,KAAK,EAAE,SACtE;IACA,IAAI,aAAa,IAAI;KACnB,QAAQ,OAAO,MAAM,yCAAyC,KAAK,GAAG;KACtE,OAAO,MAAM,IAAa;IAC5B;IACA,QAAQ,OAAO,MACb,oDAAoD,aAAa,OAAO,iBAC1E;GACF,QAAQ;IACN,QAAQ,OAAO,MAAM,kEAAkE;GACzF;EAEJ,QAAQ,CAER;EACA,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,UAAU,CAAC;EAC9D,OAAO,QAAQ,IAAI,CAAC;CACtB;CAEA,OAAO,QAAQ,CAAC;AAClB;AAEA,MAAM,oBAAoB;AAE1B,MAAM,qBAAqB,WAAkC;CAC3D,MAAM,WAAW;EACf,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB,cAAc,OAAO;CACvB;CACA,OAAOA,SAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,UAAU,QAAQ,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAC/F;AAEA,MAAM,kBAAkB,YAAoB,SAA0B;CACpE,IAAI;EACF,MAAM,YAAY,KAAK,YAAY,iBAAiB;EACpD,IAAI,CAAC,GAAG,WAAW,SAAS,GAAG,OAAO;EAEtC,OADe,KAAK,MAAM,GAAG,aAAa,WAAW,OAAO,CAChD,CAAC,CAAC,SAAS;CACzB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,oBAAoB,YAAoB,SAAuB;CACnE,IAAI;EACF,MAAM,YAAY,KAAK,YAAY,iBAAiB;EACpD,GAAG,cAAc,WAAW,KAAK,UAAU;GAAE;GAAM,WAAW,KAAK,IAAI;EAAE,CAAC,CAAC;CAC7E,QAAQ,CAER;AACF;AAKA,IAAa,gBAAb,MAA2B;CACzB;CACA,eAA4C;CAC5C,eAA2E;CAC3E,iBAAgD;CAChD,oBAA4B;CAE5B,YAAY,QAAuD;EACjE,KAAK,SAAS;GACZ,YAAY,OAAO,cAAc,KAAK,SAAS,QAAQ,GAAG,WAAW,YAAY;GACjF,SAAS,OAAO,WAAA;GAChB,UAAU,OAAO;GACjB,YAAY,OAAO;GACnB,cAAc,OAAO;GACrB,SAAS,OAAO;EAClB;CACF;CAEA,WAA2B;EACzB,OAAO,oBAAoB,KAAK,MAAM;CACxC;CAIA,eAAgC;EAC9B,OAAO,iBAAiB,KAAK,OAAO,UAAU;CAChD;CAKA,MAAc,mBAAmB,MAA6B;EAC5D,MAAM,WAAW,iBAAiB,KAAK,OAAO,UAAU,CAAC,CAAC,WAClD,cACL,MAAM,IAAI,EAAE,SACf;EACA,QAAQ,OAAO,MACb,8CAA8C,KAAK,IAAI,SAAS,OAAO,KAAK,OAAO,WAAW,UAAU,2BAE1G;EACA,KAAK,mBAAmB;EACxB,MAAM,KAAK,gBAAgB,IAAI;CACjC;CAEA,MAAc,gBAAgB,MAAc,cAAsB,IAAmB;EACnF,MAAM,UAAU,OAAO,MAA6B;GAClD,IAAI,KAAK,aAAa;IACpB,QAAQ,OAAO,MAAM,yBAAyB,KAAK,gDAAgD;IACnG;GACF;GACA,IAAI;IACF,MAAM,iBAAiB,oBAAoB,KAAK,QAAQ,GAAK;GAC/D,SAAS,KAAc;IACrB,IAAI,oBAAoB,GAAG,GAAG;GAChC;GACA,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;GACvD,OAAO,QAAQ,IAAI,CAAC;EACtB;EACA,OAAO,QAAQ,CAAC;CAClB;CAKA,gBAA8B;EAG5B,IAAI,CAAC,KAAK,cAAc;EACxB,IAAI;GACF,KAAK,aAAa,KAAK,SAAS;EAClC,QAAQ,CAER;EACA,KAAK,mBAAmB;CAC1B;CAKA,qBAAmC;EACjC,qBAAqB,KAAK,OAAO,UAAU,CAAC,CAAC,WACrC,KAAA,IACL,QAAQ;GACP,IAAI,QAAQ,QAAQ,OAAO,eAAe,GAAG,GAC3C,IAAI;IACF,QAAQ,KAAK,KAAK,SAAS;GAC7B,QAAQ,CAER;EAGJ,CACF;CACF;CAKA,kBAAgC;EAC9B,IAAI,KAAK,mBAAmB;EAC5B,KAAK,oBAAoB;EACzB,QAAQ,KAAK,cAAc,KAAK,cAAc,CAAC;EAC/C,QAAQ,KAAK,gBAAgB;GAC3B,KAAK,cAAc;GACnB,QAAQ,KAAK,CAAC;EAChB,CAAC;CACH;CAEA,MAAM,cAAqD;EACzD,MAAM,aAAa,MAAM,qBACvB,KAAK,OAAO,SACZ,KAAK,OAAO,UACZ,KAAK,OAAO,YACZ,KAAK,SAAS,CAChB;EACA,KAAK,iBAAiB;EACtB,IAAI,WAAW,YAAY,aAAa;GACtC,MAAM,WAAW,KAAK,OAAO,UAAU,oBAAoB;GAC3D,OAAO,KACL,aACE,kBACA,aAAa,KAAK,OAAO,QAAQ,GAAG,SAAS,2DAC/C,CACF;EACF;EACA,KAAK,OAAO,UAAU,WAAW;EACjC,IAAI,WAAW,iBACb,QAAQ,OAAO,MACb,oMAEF;EAEF,OAAO,MAAM,WAAW,IAAI;CAC9B;CAEA,oBAA6B;;EAC3B,SAAA,uBAAO,KAAK,oBAAA,QAAA,yBAAA,KAAA,IAAA,KAAA,IAAA,qBAAgB,aAAY,kBAAA,wBAAgB,KAAK,oBAAA,QAAA,0BAAA,KAAA,IAAA,KAAA,IAAA,sBAAgB,oBAAmB;CAClG;CAEA,MAAM,QAAqD;EAIzD,IAAI,KAAK,gBAAgB,CAAC,KAAK,aAAa,GAAG;GAC7C,QAAQ,OAAO,MAAM,6DAA6D;GAClF,KAAK,eAAe;GACpB,KAAK,eAAe;GACpB,KAAK,iBAAiB;EACxB;EACA,IAAI,KAAK,cAAc,OAAO,KAAK;EACnC,KAAK,eAAe,KAAK,QAAQ;EACjC,OAAO,KAAK;CACd;CAEA,MAAc,UAAuD;;EAEnE,MAAM,YAAY,MAAM,cAAc;EACtC,IAAI,OAAO,OAAO,SAAS,GACzB,OAAO,KACL,UAAU,MACP,MAAM,SACD,IACR,CACF;EAEF,MAAM,MAAM,UAAU,WACd,KACL,MAAM,CACT;EACA,QAAQ,OAAO,MAAM,+BAA+B,IAAI,GAAG;EAG3D,IAAI,CAAC,KAAK,gBAAgB;GACxB,MAAM,aAAa,MAAM,KAAK,YAAY;GAC1C,IAAI,OAAO,OAAO,UAAU,GAC1B,OAAO,KACL,WAAW,MACR,MAAM,SACD,IACR,CACF;EAEJ;EAIA,MAAA,wBAAI,KAAK,oBAAA,QAAA,0BAAA,KAAA,IAAA,KAAA,IAAA,sBAAgB,aAAY,kBAAkB;GACrD,QAAQ,OAAO,MACb,oEAAoE,KAAK,OAAO,QAAQ,YAC1F;GACA,OAAO,MAAM,KAAK,gBAAiB,IAAgC;EACrE;EAIA,MAAA,wBAAI,KAAK,oBAAA,QAAA,0BAAA,KAAA,IAAA,KAAA,IAAA,sBAAgB,aAAY,iBACnC,MAAM,KAAK,mBAAmB,KAAK,OAAO,OAAO;EAInD,MAAM,aAAa,kBAAkB,KAAK,MAAM;EAChD,IAAI,eAAe,KAAK,OAAO,YAAY,UAAU,GACnD,QAAQ,OAAO,MAAM,+DAA+D;OAC/E;GACL,MAAM,eAAe,MAAM,gBAAgB,KAAK,KAAK,MAAM;GAC3D,IAAI,OAAO,OAAO,YAAY,GAC5B,OAAO,KACL,aAAa,MACV,MAAM,SACD,IACR,CACF;GAEF,iBAAiB,KAAK,OAAO,YAAY,UAAU;GACnD,QAAQ,OAAO,MAAM,kDAAkD;EACzE;EAGA,IAAI,CAAC,KAAK,cAAc;GACtB,MAAM,cAAc,YAAY,KAAK,KAAK,MAAM;GAChD,IAAI,OAAO,OAAO,WAAW,GAC3B,OAAO,KACL,YAAY,MACT,MAAM,SACD,IACR,CACF;GAEF,MAAM,OAAO,YAAY,WACjB,OACL,MAAM,CACT;GACA,KAAK,eAAe;GACpB,KAAK,gBAAgB;GACrB,QAAQ,OAAO,MAAM,yCAAyC,KAAK,IAAI,IAAI;EAC7E,OACE,QAAQ,OAAO,MACb,iDAAiD,KAAK,aAAa,IAAI,uBACzE;EAIF,MAAM,cAAc,MAAM,aAAa,KAAK,OAAO,SAAS,KAAK,OAAO,UAAU,KAAK,YAAY;EACnG,IAAI,OAAO,OAAO,WAAW,GAC3B,OAAO,KACL,YAAY,MACT,MAAM,SACD,IACR,CACF;EAKF,kBAAkB,KAAK,OAAO,YAAY;GACxC,SAAS,KAAK,OAAO,WAAW;GAChC,UAAU,KAAK,SAAS;EAC1B,CAAC;EAED,OAAO,MAAM,KAAK,YAAa;CACjC;CAEA,MAAM,OAAqC;EAEzC,KAAK,eAAe;EAEpB,IAAI,CAAC,KAAK,cAAc,OAAO,MAAM,IAAa;EAElD,MAAM,OAAO,KAAK;EAClB,IAAI;GACF,KAAK,KAAK,SAAS;GAEnB,MAAM,IAAI,SAAe,YAAY;IACnC,MAAM,UAAU,iBAAiB;KAC/B,KAAK,KAAK,SAAS;KACnB,QAAQ;IACV,GAAG,GAAI;IAEP,KAAK,GAAG,cAAc;KACpB,aAAa,OAAO;KACpB,QAAQ;IACV,CAAC;GACH,CAAC;GAED,KAAK,mBAAmB;GACxB,KAAK,eAAe;GACpB,QAAQ,OAAO,MAAM,mCAAmC;GACxD,OAAO,MAAM,IAAa;EAC5B,SAAS,OAAO;GACd,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,MAAM,cAA4C;EAChD,IAAI;GACF,MAAM,WAAW,MAAM,iBAAiB,oBAAoB,KAAK,OAAO,QAAQ,QAAQ,GAAK;GAC7F,IAAI,CAAC,SAAS,IAAI,OAAO,qBAAK,IAAI,MAAM,wBAAwB,SAAS,QAAQ,CAAC;GAClF,OAAO,MAAM,IAAa;EAC5B,SAAS,OAAO;GACd,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,UAAkB;EAChB,OAAO,KAAK,OAAO;CACrB;CAEA,UAAkB;EAChB,OAAO;CACT;AACF;;;AChyBA,MAAM,cAAc,MAClB,EACG,QAAQ,iBAAiB,GAAG,SAAS,QAAQ,IAAI,SAAS,EAAE,CAAC,CAC7D,QAAQ,aAAa,GAAG,SAAS,QAAQ,IAAI,SAAS,EAAE;AAE7D,MAAM,iBAAiB,MAAuB;CAC5C,IAAI;EAEF,OADgB,GAAG,YAAY,CAClB,CAAC,CAAC,SAAS;CAC1B,QAAQ;EACN,OAAO;CACT;AACF;AAKA,MAAM,kBAAkB,WAAmB,mBAAmC;CAC5E,IAAI,CAAC,SAAS,MAAM,GAAG,OAAO;CAC9B,IAAI,GAAG,WAAW,SAAS,KAAK,cAAc,SAAS,GAAG,OAAO;CACjE,OAAO,SAAS,eAAe,CAAC,CAC7B,KAAK,YAAY,KAAK,SAAS,cAAc,CAAC,CAAC,CAC/C,OAAO,aAAa,CAAC,CACrB,KAAK,YAAY;EAChB,QAAQ,OAAO,MAAM,cAAc,UAAU,sCAAsC,QAAQ,GAAG;EAC9F,OAAO;CACT,CAAC,CAAC,CACD,OAAO,SAAS;AACrB;AAEA,MAAM,cAAc,MAAsB;CACxC,MAAM,WAAW,WAAW,CAAC;CAC7B,MAAM,gBAAgB,KAAK,YAAY,QAAQ;CAC/C,IAAI,aAAa,OAAO,SAAS,WAAW,IAAI,GAAG;EACjD,MAAM,iBAAiB,aAAa,MAAM,KAAK,SAAS,MAAM,CAAC;EAC/D,OAAO,eAAe,eAAe,cAAc;CACrD;CACA,OAAO;AACT;AAEA,MAAM,cAAc,MAAgB,SAAiC;CACnE,MAAM,QAAQ,KAAK,QAAQ,IAAI;CAC/B,IAAI,UAAU,IAAI,OAAO,OAAO,KAAK;CACrC,MAAM,QAAQ,KAAK,QAAQ;CAC3B,IAAI,CAAC,SAAS,MAAM,WAAW,IAAI,GAAG,OAAO,OAAO,KAAK;CACzD,KAAK,OAAO,OAAO,CAAC;CACpB,OAAO,OAAO,KAAK;AACrB;AAEA,MAAa,mBAAmB,SAKE;CAChC,MAAM,aAAa,KAAK,WAAW,OAAO,MAAM;CAEhD,QAAQ,YAAR;EACE,KAAK,QACH,OAAO,MAAM,EAAE,MAAM,OAAO,CAAe;EAE7C,KAAK,cACH,OAAO,KAAK,SAAS,WACb,KAAK,iDAAiD,IAC3D,SAAS,MAAM;GAAE,MAAM;GAAc;EAAK,CAAe,CAC5D;EAEF,KAAK,UACH,OAAO,KAAK,SAAS,WACb,KAAK,6CAA6C,IACvD,QACC,KAAK,aAAa,WACV,KAAK,iDAAiD,IAC3D,aACC,KAAK,aAAa,WACV,KAAK,iDAAiD,IAC3D,aAAa,MAAM;GAAE,MAAM;GAAU;GAAK;GAAU;EAAS,CAAe,CAC/E,CACJ,CACJ;EAEF,KAAK,aACH,OAAO,KAAK,SAAS,WACb,KAAK,gDAAgD,IAC1D,QACC,KAAK,aAAa,WACV,KAAK,oDAAoD,IAC9D,aACC,KAAK,aAAa,WACV,KAAK,oDAAoD,IAC9D,aAAa,MAAM;GAAE,MAAM;GAAa;GAAK;GAAU;EAAS,CAAe,CAClF,CACJ,CACJ;EAEF,KAAK,gBACH,OAAO,KAAK,aAAa,WACjB,KAAK,uDAAuD,IACjE,UACC,KAAK,aAAa,WACV,KAAK,uDAAuD,IACjE,aAAa,MAAM;GAAE,MAAM;GAAgB;GAAO;EAAS,CAAe,CAC7E,CACJ;EAEF,KAAK,iBACH,OAAO,KAAK,SAAS,WACb,KAAK,oDAAoD,IAC9D,QACC,KAAK,aAAa,WACV,KAAK,wDAAwD,IAClE,UACC,KAAK,aAAa,WACV,KAAK,wDAAwD,IAClE,aAAa,MAAM;GAAE,MAAM;GAAiB;GAAK;GAAO;EAAS,CAAe,CACnF,CACJ,CACJ;EAEF,KAAK,MACH,OAAO,KAAK,SAAS,WACb,KAAK,kDAAkD,IAC5D,WACC,KAAK,aAAa,WACV,KAAK,0DAA0D,IACpE,cACC,KAAK,aAAa,WACV,KAAK,0DAA0D,IACpE,cAAc,MAAM;GAAE,MAAM;GAAM;GAAQ,QAAQ;GAAa;GAAW;EAAU,CAAe,CACtG,CACJ,CACJ;EAEF,KAAK,WACH,OAAO,MAAM,EAAE,MAAM,UAAU,CAAe;EAEhD,KAAK,YACH,OAAO,MAAM,EAAE,MAAM,WAAW,CAAe;EAEjD,SACE,OAAO,KACL,wBAAwB,WAAW,yGACrC;CACJ;AACF;AAEA,SAAS,YAAwB;CAC/B,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CAGjC,MAAM,eAAe,YAAoB;EACvC,IAAI;GACF,IAAI,GAAG,WAAW,OAAO,GAAG;IAC1B,QAAQ,OAAO,MAAM,6BAA6B,QAAQ,GAAG;IAE7D,MAAM,WADa,GAAG,aAAa,SAAS,OAClB,CAAC,CAAC,MAAM,IAAI;IACtC,MAAM,aAAuB,CAAC;IAE9B,KAAK,MAAM,QAAQ,UAAU;KAC3B,MAAM,cAAc,KAAK,KAAK;KAC9B,IAAI,eAAe,CAAC,YAAY,WAAW,GAAG,GAAG;MAC/C,MAAM,CAAC,KAAK,GAAG,cAAc,YAAY,MAAM,GAAG;MAClD,IAAI,OAAO,WAAW,SAAS,GAAG;OAChC,MAAM,QAAQ,WAAW,KAAK,GAAG,CAAC,CAAC,QAAQ,gBAAgB,EAAE;OAC7D,IAAI,CAAC,QAAQ,IAAI,IAAI,KAAK,IAAI;QAC5B,QAAQ,IAAI,IAAI,KAAK,KAAK;QAC1B,WAAW,KAAK,IAAI,KAAK,CAAC;OAC5B;MACF;KACF;IACF;IAEA,IAAI,WAAW,SAAS,GACtB,QAAQ,OAAO,MAAM,qBAAqB,WAAW,KAAK,IAAI,EAAE,GAAG;GAEvE;EACF,SAAS,OAAgB;GACvB,QAAQ,OAAO,MAAM,mCAAmC,MAAM,GAAG;EACnE;CACF;CAIA,WAD2B,MAAM,YAC3B,CAAC,CAAC,WACA,YAAY,MAAM,IACvB,SAAS,YAAY,QAAQ,QAAQ,IAAI,GAAG,IAAI,CAAC,CACpD;CAIA,MAAM,gBAAgB,WAAW,MAAM,SAAS;CAGhD,MAAM,YAA8B,WAAW,MAAM,aAAa,CAAC,CAChE,KAAK,UAA4B;EAChC,IAAI,UAAU,WAAW,UAAU,QAAQ;GACzC,QAAQ,OAAO,MAAM,uDAAuD;GAC5E,QAAQ,KAAK,CAAC;EAChB;EACA,OAAO;CACT,CAAC,CAAC,CACD,OAAO,OAAO;CAGjB,MAAM,WAAmB,WAAW,MAAM,aAAa,CAAC,CACrD,KAAK,UAAU;EACd,MAAM,SAAS,SAAS,OAAO,EAAE;EACjC,IAAI,MAAM,MAAM,KAAK,SAAS,KAAK,SAAS,OAAO;GACjD,QAAQ,OAAO,MAAM,4DAA4D;GACjF,QAAQ,KAAK,CAAC;EAChB;EACA,OAAO;CACT,CAAC,CAAC,CACD,OAAO,GAAI;CAGd,MAAM,aAAa,WAAW,MAAM,WAAW,CAAC,CAC7C,GAAG,OAAO,QAAQ,IAAI,cAAc,CAAC,CAAC,CACtC,IAAI,UAAU,CAAC,CACf,OAAO,WAAW,sBAAsB,CAAC;CAG5C,MAAM,aAAa,WAAW,MAAM,eAAe,CAAC,CAAC,GAAG,OAAO,QAAQ,IAAI,kBAAkB,CAAC;CAC9F,MAAM,WAAW,WAAW,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,QAAQ,IAAI,gBAAgB,CAAC,CAAC,CAAC,IAAI,UAAU;CACxG,MAAM,eAAe,WAAW,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,QAAQ,IAAI,oBAAoB,CAAC;CACpG,MAAM,eAAe,WAAW,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,QAAQ,IAAI,oBAAoB,CAAC;CAGpG,MAAM,aAAa,gBAAgB;EAAE;EAAY;EAAU;EAAc;CAAa,CAAC;CACvF,IAAI,OAAO,OAAO,UAAU,GAAG;EAC7B,MAAM,MAAM,WAAW,MACpB,MAAM,SACD,EACR;EACA,QAAQ,OAAO,MAAM,UAAU,IAAI,GAAG;EACtC,QAAQ,KAAK,CAAC;CAChB;CACA,MAAM,kBAAkB,WAAW,YAC1B,EAAE,MAAM,OAAO,KACrB,MAAM,CACT;CACA,MAAM,qBACJ,gBAAgB,SAAS,SAAS,OAAO,KAAiB,IAAI,OAAO,eAA6B;CAGpG,IAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;EAClD,QAAQ,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoExB;EACG,QAAQ,KAAK,CAAC;CAChB;CAEA,OAAO;EACL,eAAe;EACf;EACA;EACA;EACA,YAAY;EACZ;CACF;AACF;;;ACnUA,MAAa,sBAAsB,UAA8C;CAC/E,IAAI,MAAM,cAAc,YAAY,OAClC,OAAO;EAAE,QAAQ;EAAY,gBAAgB;CAAM;CAErD,IAAI,MAAM,cACR,OAAO;EACL,QAAQ,MAAM,aAAa,YAAY,QAAQ,iBAAiB;EAChE,gBAAgB;CAClB;CAEF,OAAO;EAAE,QAAQ;EAAW,gBAAgB,MAAM,aAAa,YAAY;CAAM;AACnF;AAGA,MAAa,wBAAwB,UACnC,MAAM,gBAAgB,MAAM,cAAc,WAAW,MAAM,aAAa;;;;;;;;AC/B1E,IAAM,YAAN,cAAwB,MAAM;CAC5B,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;;;AAQA,MAAM,6BAA6B,SAAsC;CACvE,IAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,WAAW,MAAM;EAChE,MAAM,MAAO,KAA4B;EACzC,IAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG,OAAO;CACxD;AAEF;AAoBA,IAAe,WAAf,MAAwB;CACtB;CAEA,YAAY,WAA4B;EACtC,KAAK,YAAY;CACnB;CAIA,YAAsB,OAAgB,SAAyB;EAC7D,QAAQ,OAAO,MAAM,GAAG,QAAQ,UAAU,MAAM,GAAG;EACnD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;EACzD,OAAO,SAAS,QAAQ,YAAY,EAAE,IAAI;CAC5C;CAEA,WAAqB,IAAY,MAA0C;EACzE,IAAI,CAAC,IACH,OAAO,oBAAoB,KAAK,gBAAgB,SAAS,SAAS,cAAc,gBAAgB,GAAG,KAAK,YAAY,KAAK;EAG3H,IAAI,GAAG,SAAS,MAAM,CAAC,GAAG,MAAM,WAAW,GAAG;GAC5C,MAAM,aAAa,SAAS,SAAS,iBAAiB;GACtD,OAAO,WAAW,GAAG,kCAAkC,KAAK,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,qFAAqF,WAAW,MAAM,SAAS,SAAS,eAAe,8BAA8B;EAC3R;EAEA,OAAO;CACT;CAEA,OAAoB,QAA6B;EAC/C,OAAO,OAAO,MACX,QAAQ;GACP,MAAM;EACR,IACC,QAAQ,GACX;CACF;CAEA,WAAqB,WAA2B;EAC9C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,eAAe;CAC5C;AACF;;;AC3EA,IAAM,eAAN,cAA2B,SAAS;CAClC,MAAM,KAAK,SAA+C;EACxD,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;EAIT,IAAI,CAAC,QAAQ,SAAS,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAAM,KAAK,MAAM,IAClF,OAAO;EAIT,IAAI,QAAQ,cAAc,QAAQ,UAAU,SAAS,MAAM,CAAC,QAAQ,UAAU,MAAM,WAAW,IAC7F,OAAO,WAAW,QAAQ,UAAU;EAGtC,IAAI;GAEF,MAAM,cAAmC,EACvC,OAAO,QAAQ,MAAM,KAAK,EAC5B;GAEA,IAAI,QAAQ,WACV,YAAY,YAAY,QAAQ;GAIlC,MAAM,gBAAgB,KAAK,OAAO,MAAM,KAAK,UAAU,KAA2B,YAAY,WAAW,CAAC;GAI1G,MAAM,cAAc,0BAA0B,aAAa;GAC3D,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,UAAU,8BAA8B,aAAa;GAEjE,IAAI,CAAC,iBAAiB,OAAO,kBAAkB,YAAY,CAAC,cAAc,IACxE,MAAM,IAAI,UACR,uGAAuG,KAAK,UAAU,aAAa,GACrI;GAIF,MAAM,aAAa,OAAO,YAA6B;IACrD,IAAI,CAAC,cAAc,WAAW,OAAO;IACrC,IAAI;KACF,MAAM,iBAAiB,KAAK,OAC1B,MAAM,KAAK,UAAU,IAAkB,YAAY,cAAc,aAAa,EAC5E,OAAO,EAAE,QAAQ,WAAW,EAC9B,CAAC,CACH;KACA,IAAA,mBAAA,QAAA,mBAAA,KAAA,IAAA,KAAA,IAAI,eAAgB,OAClB,OAAO,WAAW,eAAe,MAAM,mBAAmB,cAAc,UAAU;KAEpF,OAAO,uBAAuB,cAAc;IAC9C,QAAQ;KACN,OAAO,uBAAuB,cAAc;IAC9C;GACF,EAAA,CAAG;GAGH,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,kCAAkC;GACnD,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,sBAAsB;GACvC,YAAY,KAAK,cAAc,cAAc,MAAM,EAAE;GACrD,YAAY,KAAK,mBAAmB,cAAc,IAAI;GACtD,YAAY,KAAK,gBAAgB,YAAY;GAE7C,MAAM,cAAc,KAAK,WAAW,cAAc,YAAY;GAC9D,YAAY,KAAK,eAAe,aAAa;GAE7C,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,gBAAgB;GACjC,YAAY,KAAK,kDAAkD,cAAc,GAAG,EAAE;GACtF,YAAY,KAAK,4EAA4E,cAAc,GAAG,GAAG;GACjH,YAAY,KAAK,yCAAyC;GAE1D,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,4BAA4B,MAAM,GAAG;GAC1D,IAAI,IAAI,UAAU;IAChB,IAAI,IAAI,SAAS,WAAW,KAAK;;KAC/B,MAAM,IAAI,UACR,sDAAA,qBAAoD,IAAI,SAAS,UAAA,QAAA,uBAAA,KAAA,IAAA,KAAA,IAAA,mBAAM,UAAS,uCAClF;IACF;IACA,IAAI,IAAI,SAAS,WAAW,OAAO,QAAQ,cAAc,KAAA,GACvD,MAAM,IAAI,UACR,uDAAuD,QAAQ,UAAU,4HAC3E;IAEF,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,yDAAyD,QAAQ,MAAM,+GACzE;GAEJ;GAEA,MAAM,IAAI,UAAU,8BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACE;EAC7D;CACF;AACF;;;ACvGA,IAAM,aAAN,cAAyB,SAAS;CAChC,MAAM,KAAK,SAA6C;EACtD,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;EAIT,IAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,QAAQ,CAAC,QAAQ,WAC9C,OAAO;EAIT,IAAI,QAAQ,cAAc,QAAQ,UAAU,SAAS,MAAM,CAAC,QAAQ,UAAU,MAAM,WAAW,IAC7F,OAAO,WAAW,QAAQ,UAAU;EAGtC,IAAI;GAEF,MAAM,cAAiC,CAAC;GAExC,IAAI,QAAQ,OAAO,YAAY,QAAQ,QAAQ;GAC/C,IAAI,QAAQ,MAAM,YAAY,OAAO,QAAQ;GAC7C,IAAI,QAAQ,WAAW,YAAY,YAAY,QAAQ;GACvD,IAAI,QAAQ,WAAW,YAAY,YAAY,QAAQ;GACvD,IAAI,QAAQ,YAAY,KAAA,GAAW,YAAY,UAAU,QAAQ;GACjE,IAAI,QAAQ,gBAAgB,YAAY,iBAAiB,QAAQ;GAGjE,MAAM,cAAc,KAAK,OAAO,MAAM,KAAK,UAAU,KAAyB,UAAU,WAAW,CAAC;GAKpG,MAAM,cAAc,0BAA0B,WAAW;GACzD,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,UAAU,0BAA0B,aAAa;GAE7D,IAAI,CAAC,eAAe,OAAO,gBAAgB,YAAY,CAAC,YAAY,IAClE,MAAM,IAAI,UACR,iGAAiG,KAAK,UAAU,WAAW,GAC7H;GAIF,MAAM,eAAe,OAAO,YAA6B;IACvD,IAAI,CAAC,YAAY,WAAW,OAAO;IACnC,IAAI;KACF,MAAM,WAAW,KAAK,OACpB,MAAM,KAAK,UAAU,IAAkB,YAAY,YAAY,aAAa,EAC1E,OAAO,EAAE,QAAQ,WAAW,EAC9B,CAAC,CACH;KACA,IAAA,aAAA,QAAA,aAAA,KAAA,IAAA,KAAA,IAAI,SAAU,OACZ,OAAO,IAAI,SAAS,MAAM,mBAAmB,YAAY,UAAU;KAErE,OAAO,gBAAgB,YAAY;IACrC,QAAQ;KACN,OAAO,gBAAgB,YAAY;IACrC;GACF,EAAA,CAAG;GAGH,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,8BAA8B;GAC/C,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,kBAAkB;GACnC,YAAY,KAAK,cAAc,YAAY,SAAS,WAAW,EAAE;GACjE,YAAY,KAAK,eAAe,YAAY,IAAI;GAChD,YAAY,KAAK,gBAAgB,cAAc;GAE/C,IAAI,YAAY,SACd,YAAY,KAAK,oBAAoB;GAGvC,MAAM,cAAc,KAAK,WAAW,YAAY,YAAY;GAC5D,YAAY,KAAK,eAAe,aAAa;GAE7C,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,gBAAgB;GACjC,YAAY,KAAK,0CAA0C,YAAY,GAAG,EAAE;GAC5E,IAAI,YAAY,WACd,YAAY,KAAK,kDAAkD,YAAY,UAAU,EAAE;GAE7F,YAAY,KAAK,2CAA2C,YAAY,MAAM,EAAE;GAEhF,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GAEvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,wBAAwB,MAAM,GAAG;GACtD,IAAI,IAAI,UAAU;IAChB,IAAI,IAAI,SAAS,WAAW,KAAK;;KAC/B,MAAM,IAAI,UACR,kDAAA,qBAAgD,IAAI,SAAS,UAAA,QAAA,uBAAA,KAAA,IAAA,KAAA,IAAA,mBAAM,UAAS,uCAC9E;IACF;IACA,IAAI,IAAI,SAAS,WAAW,OAAO,QAAQ,cAAc,KAAA,GACvD,MAAM,IAAI,UACR,4CAA4C,QAAQ,UAAU,0EAChE;GAEJ;GAEA,MAAM,IAAI,UAAU,0BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACF;EACzD;CACF;AACF;;;AC3GA,IAAM,eAAN,cAA2B,SAAS;CAClC,MAAM,KAAK,SAA+C;EACxD,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;EAIT,IAAI,CAAC,QAAQ,WACX,OAAO;EAGT,MAAM,gBAAgB,KAAK,WAAW,QAAQ,WAAW,UAAU;EACnE,IAAI,eACF,OAAO,cAAc,QAAQ,eAAe,WAAW,CAAC,CAAC,QAAQ,eAAe,WAAW;EAI7F,IAAI,CAAC,QAAQ,SACX,OAAO,oHAAoH,QAAQ,UAAU;EAG/I,IAAI;GAEF,MAAM,iBAAiB,KAAK,OAC1B,MAAM,KAAK,UAAU,IAAkB,YAAY,QAAQ,aAAa,EACtE,OAAO,EAAE,QAAQ,qBAAqB,EACxC,CAAC,CACH;GAEA,MAAM,kBAAkB,0BAA0B,cAAc;GAChE,IAAI,oBAAoB,KAAA,GACtB,MAAM,IAAI,UAAU,0BAA0B,QAAQ,UAAU,KAAK,iBAAiB;GAExF,IAAI,EAAA,mBAAA,QAAA,mBAAA,KAAA,IAAA,KAAA,IAAC,eAAgB,KACnB,MAAM,IAAI,UACR,mBAAmB,QAAQ,UAAU,wEACvC;GAIF,MAAM,CAAC,OAAO,cAAc,MAAM,QAAQ,IAAI,CAC5C,KAAK,UACF,IAAoB,YAAY,QAAQ,UAAU,SAAS,EAC1D,OAAO,EAAE,QAAQ,WAAW,EAC9B,CAAC,CAAC,CACD,MAAM,WACL,OAAO,YACE,EAAE,OAAO,CAAC,EAAE,KAClB,SAAS,IACZ,CACF,GACF,KAAK,UACF,IAAoB,YAAY,EAC/B,OAAO,EAAE,QAAQ,qBAAqB,EACxC,CAAC,CAAC,CACD,MAAM,WACL,OAAO,YACE,EAAE,OAAO,CAAC,EAAE,KAClB,cAAc,EACb,OAAO,SAAS,MAAM,QAAQ,WAAW,OAAO,cAAc,QAAQ,SAAS,EACjF,EACF,CACF,CACJ,CAAC;GAED,MAAM,YAAY,MAAM,MAAM;GAC9B,MAAM,iBAAiB,WAAW,MAAM;GACxC,MAAM,eAAe,YAAY;GAGjC,IAAI,eAAe,KAAK,CAAC,QAAQ,OAAO;IACtC,MAAM,cAAwB,CAAC;IAC/B,YAAY,KAAK,uCAAuC;IACxD,YAAY,KAAK,EAAE;IACnB,YAAY,KAAK,iBAAiB,eAAe,MAAM,EAAE;IACzD,YAAY,KAAK,gBAAgB,UAAU,aAAa,eAAe,YAAY;IAEnF,IAAI,YAAY,GAAG;KACjB,YAAY,KAAK,EAAE;KACnB,YAAY,KAAK,eAAe,UAAU,QAAQ;KAClD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,SAAS;MACxC,YAAY,KAAK,QAAQ,KAAK,SAAS,YAAY;KACrD,CAAC;KACD,IAAI,YAAY,GACd,YAAY,KAAK,cAAc,YAAY,EAAE,YAAY;IAE7D;IAEA,IAAI,iBAAiB,GAAG;KACtB,YAAY,KAAK,EAAE;KACnB,YAAY,KAAK,eAAe,eAAe,aAAa;KAC5D,WAAW,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,WAAW;MAC/C,YAAY,KAAK,QAAQ,OAAO,SAAS,YAAY;KACvD,CAAC;KACD,IAAI,iBAAiB,GACnB,YAAY,KAAK,cAAc,iBAAiB,EAAE,cAAc;IAEpE;IAEA,YAAY,KAAK,EAAE;IACnB,YAAY,KAAK,aAAa;IAC9B,YAAY,KAAK,iEAAiE;IAClF,YAAY,KAAK,gDAAgD;IACjE,YAAY,KAAK,sCAAsC,QAAQ,UAAU,mCAAmC;IAC5G,YAAY,KAAK,EAAE;IACnB,YAAY,KAAK,gDAAgD,aAAa,eAAe;IAE7F,OAAO,YAAY,KAAK,IAAI;GAC9B;GAGA,MAAM,aAAa,OAAO,YAA6B;IACrD,IAAI,CAAC,eAAe,WAAW,OAAO;IACtC,IAAI;KACF,MAAM,eAAe,KAAK,OACxB,MAAM,KAAK,UAAU,IAAkB,YAAY,eAAe,aAAa,EAC7E,OAAO,EAAE,QAAQ,QAAQ,EAC3B,CAAC,CACH;KACA,IAAA,iBAAA,QAAA,iBAAA,KAAA,IAAA,KAAA,IAAI,aAAc,OAChB,OAAO,WAAW,aAAa,MAAM,mBAAmB,eAAe,UAAU;KAEnF,OAAO,cAAc,eAAe;IACtC,QAAQ;KACN,OAAO,cAAc,eAAe;IACtC;GACF,EAAA,CAAG;GAIH,MAAM,cAAc,0BADG,KAAK,OAAO,MAAM,KAAK,UAAU,OAAO,YAAY,QAAQ,WAAW,CACnC,CAAC;GAC5D,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,UAAU,4BAA4B,aAAa;GAI/D,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,qCAAqC;GACtD,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,8BAA8B;GAC/C,YAAY,KAAK,cAAc,eAAe,MAAM,EAAE;GACtD,YAAY,KAAK,iBAAiB,eAAe,IAAI;GACrD,YAAY,KAAK,gBAAgB,YAAY;GAE7C,IAAI,eAAe,GAAG;IACpB,YAAY,KAAK,uBAAuB,UAAU,aAAa,eAAe,YAAY;IAC1F,YAAY,KAAK,EAAE;IACnB,YAAY,KAAK,WAAW,aAAa,6CAA6C;GACxF;GAEA,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,yEAAyE;GAE1F,IAAI,eAAe,WAAW;IAC5B,YAAY,KAAK,EAAE;IACnB,YAAY,KAAK,qBAAqB;IACtC,YAAY,KAAK,yDAAyD,eAAe,UAAU,EAAE;IACrG,YAAY,KAAK,yCAAyC;GAC5D;GAEA,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,0BAA0B,MAAM,GAAG;GACxD,IAAI,IAAI,UAAU;IAChB,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,4CAA4C,QAAQ,UAAU,wEAChE;IAEF,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,kEAAkE,QAAQ,UAAU,4CACtF;IAEF,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,sIACF;GAEJ;GAEA,MAAM,IAAI,UAAU,4BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACA;EAC3D;CACF;AACF;;;AClMA,IAAM,aAAN,cAAyB,SAAS;CAChC,MAAM,KAAK,SAA6C;EACtD,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;EAIT,IAAI,CAAC,QAAQ,SACX,OAAO;EAGT,MAAM,cAAc,KAAK,WAAW,QAAQ,SAAS,MAAM;EAC3D,IAAI,aACF,OAAO;EAIT,IAAI,CAAC,QAAQ,SACX,OAAO,qGAAqG,QAAQ,QAAQ;EAG9H,IAAI;GAEF,MAAM,eAAe,KAAK,OACxB,MAAM,KAAK,UAAU,IAAgB,UAAU,QAAQ,WAAW,EAChE,OAAO,EAAE,QAAQ,2EAA2E,EAC9F,CAAC,CACH;GAEA,MAAM,gBAAgB,0BAA0B,YAAY;GAC5D,IAAI,kBAAkB,KAAA,GACpB,MAAM,IAAI,UAAU,wBAAwB,QAAQ,QAAQ,KAAK,eAAe;GAElF,IAAI,EAAA,iBAAA,QAAA,iBAAA,KAAA,IAAA,KAAA,IAAC,aAAc,KACjB,MAAM,IAAI,UACR,iBAAiB,QAAQ,QAAQ,2DACnC;GAIF,MAAM,eAAe,OAAO,YAA6B;IACvD,IAAI,CAAC,aAAa,WAAW,OAAO;IACpC,IAAI;KACF,MAAM,WAAW,KAAK,OACpB,MAAM,KAAK,UAAU,IAAkB,YAAY,aAAa,aAAa,EAC3E,OAAO,EAAE,QAAQ,QAAQ,EAC3B,CAAC,CACH;KACA,IAAA,aAAA,QAAA,aAAA,KAAA,IAAA,KAAA,IAAI,SAAU,OACZ,OAAO,IAAI,SAAS,MAAM,mBAAmB,aAAa,UAAU;KAEtE,OAAO,gBAAgB,aAAa;IACtC,QAAQ;KACN,OAAO,gBAAgB,aAAa;IACtC;GACF,EAAA,CAAG;GAIH,MAAM,cAAc,0BADG,KAAK,OAAO,MAAM,KAAK,UAAU,OAAO,UAAU,QAAQ,SAAS,CAC/B,CAAC;GAC5D,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,UAAU,0BAA0B,aAAa;GAI7D,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,iCAAiC;GAClD,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,0BAA0B;GAC3C,YAAY,KAAK,cAAc,aAAa,SAAS,WAAW,EAAE;GAClE,YAAY,KAAK,eAAe,aAAa,IAAI;GACjD,YAAY,KAAK,gBAAgB,cAAc;GAE/C,IAAI,aAAa,SAAS;IACxB,MAAM,SAAS,aAAa,iBAAiB,cAAc;IAC3D,YAAY,KAAK,kBAAkB,OAAO,EAAE;GAC9C,OACE,YAAY,KAAK,uBAAuB;GAG1C,MAAM,cAAc,KAAK,WAAW,aAAa,YAAY;GAC7D,MAAM,cAAc,KAAK,WAAW,aAAa,YAAY;GAC7D,YAAY,KAAK,eAAe,aAAa;GAC7C,YAAY,KAAK,oBAAoB,aAAa;GAGlD,IAAI,aAAa,MAAM;IACrB,MAAM,UAAU,aAAa,KAAK,UAAU,GAAG,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG;IACtE,MAAM,YAAY,aAAa,KAAK,SAAS,MAAM,QAAQ;IAC3D,YAAY,KAAK,uBAAuB,UAAU,WAAW;GAC/D;GAEA,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,qEAAqE;GAEtF,IAAI,aAAa,WAAW;IAC1B,YAAY,KAAK,EAAE;IACnB,YAAY,KAAK,qBAAqB;IACtC,YAAY,KAAK,6DAA6D,aAAa,UAAU,EAAE;IACvG,YAAY,KAAK,sDAAsD,aAAa,MAAM,EAAE;GAC9F;GAEA,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,wBAAwB,MAAM,GAAG;GACtD,IAAI,IAAI,UAAU;IAChB,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,wCAAwC,QAAQ,QAAQ,2DAC1D;IAEF,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,8DAA8D,QAAQ,QAAQ,0CAChF;GAEJ;GAEA,MAAM,IAAI,UAAU,0BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACF;EACzD;CACF;AACF;;;ACvHA,IAAM,aAAN,cAAyB,SAAS;CAChC,MAAM,KAAK,SAA6C;EACtD,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;EAIT,IAAI,CAAC,QAAQ,WACX,OAAO;EAGT,MAAM,gBAAgB,KAAK,WAAW,QAAQ,WAAW,UAAU;EACnE,IAAI,eACF,OAAO,cAAc,QAAQ,eAAe,WAAW,CAAC,CAAC,QAAQ,eAAe,WAAW;EAO7F,IAAI,CAFc,CADI,SAAS,WACF,CAAC,CAAC,MAAM,UAAU,QAAQ,WAAsC,KAAA,CAEhF,GACX,OAAO;EAIT,IAAI,QAAQ,UAAU,KAAA,MAAc,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAAM,KAAK,MAAM,KAChG,OAAO;EAIT,IAAI,QAAQ,cAAc,KAAA,KAAa,QAAQ,cAAc,QAAQ,QAAQ,cAAc,IAAI;GAC7F,IAAI,QAAQ,UAAU,SAAS,MAAM,CAAC,QAAQ,UAAU,MAAM,WAAW,GACvE,OAAO,WAAW,QAAQ,UAAU;GAItC,IAAI,QAAQ,cAAc,QAAQ,WAChC,OAAO;EAEX;EAEA,IAAI;GAEF,MAAM,gBAAgB,KAAK,OACzB,MAAM,KAAK,UAAU,IAAkB,YAAY,QAAQ,aAAa,EACtE,OAAO,EAAE,QAAQ,qBAAqB,EACxC,CAAC,CACH;GAEA,MAAM,qBAAqB,0BAA0B,aAAa;GAClE,IAAI,uBAAuB,KAAA,GACzB,MAAM,IAAI,UAAU,0BAA0B,QAAQ,UAAU,KAAK,oBAAoB;GAE3F,IAAI,EAAA,kBAAA,QAAA,kBAAA,KAAA,IAAA,KAAA,IAAC,cAAe,KAClB,MAAM,IAAI,UACR,mBAAmB,QAAQ,UAAU,wEACvC;GAIF,MAAM,aAAyC,CAAC;GAEhD,IAAI,QAAQ,UAAU,KAAA,GAAW,WAAW,QAAQ,QAAQ,MAAM,KAAK;GACvE,IAAI,QAAQ,cAAc,KAAA,GAAW,WAAW,YAAY,QAAQ;GAGpE,MAAM,gBAAgB,KAAK,OACzB,MAAM,KAAK,UAAU,IAAwB,YAAY,QAAQ,aAAa,UAAU,CAC1F;GAIA,MAAM,cAAc,0BAA0B,aAAa;GAC3D,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,UAAU,4BAA4B,aAAa;GAE/D,IAAI,CAAC,iBAAiB,OAAO,kBAAkB,YAAY,CAAC,cAAc,IACxE,MAAM,IAAI,UACR,qGAAqG,KAAK,UAAU,aAAa,GACnI;GAIF,MAAM,kBAAkB,OAAO,aAAsC;IACnE,IAAI;KACF,MAAM,SAAS,KAAK,OAClB,MAAM,KAAK,UAAU,IAAkB,YAAY,YAAY,EAC7D,OAAO,EAAE,QAAQ,QAAQ,EAC3B,CAAC,CACH;KACA,IAAA,WAAA,QAAA,WAAA,KAAA,IAAA,KAAA,IAAI,OAAQ,OACV,OAAO,WAAW,OAAO,MAAM;KAEjC,OAAO,cAAc;IACvB,QAAQ;KACN,OAAO,cAAc;IACvB;GACF;GAEA,MAAM,gBAAgB,cAAc,YAAY,MAAM,gBAAgB,cAAc,SAAS,IAAI;GACjG,MAAM,gBAAgB,OAAO,YAA6B;IACxD,IAAI,CAAC,cAAc,WAAW,OAAO;IACrC,IAAI,cAAc,cAAc,cAAc,WAAW,OAAO;IAChE,OAAO,gBAAgB,cAAc,SAAS;GAChD,EAAA,CAAG;GAGH,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,kCAAkC;GACnD,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,iBAAiB,cAAc,MAAM,EAAE;GACxD,YAAY,KAAK,iBAAiB,cAAc,IAAI;GACpD,YAAY,KAAK,EAAE;GAGnB,YAAY,KAAK,kBAAkB;GAEnC,IAAI,QAAQ,UAAU,KAAA,KAAa,cAAc,UAAU,cAAc,OACvE,YAAY,KAAK,cAAc,cAAc,MAAM,OAAO,cAAc,MAAM,EAAE;GAGlF,IAAI,QAAQ,cAAc,KAAA,KAAa,cAAc,cAAc,cAAc,WAC/E,YAAY,KAAK,gBAAgB,cAAc,KAAK,eAAe;GAGrE,IAAI,cAAc,cAAc;IAC9B,MAAM,cAAc,KAAK,WAAW,cAAc,YAAY;IAC9D,YAAY,KAAK,oBAAoB,aAAa;GACpD;GAEA,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,gBAAgB;GACjC,YAAY,KAAK,kDAAkD,cAAc,GAAG,EAAE;GACtF,YAAY,KAAK,yCAAyC;GAC1D,IAAI,cAAc,WAChB,YAAY,KAAK,yDAAyD,cAAc,UAAU,EAAE;GAGtG,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GAIZ,QAAQ,OAAO,MAAM,0BAA0B,MAAM,GAAG;GACxD,IAAI,IAAI,UAAU;IAChB,IAAI,IAAI,SAAS,WAAW,KAAK;;KAC/B,MAAA,cAAI,IAAI,YAAA,QAAA,gBAAA,KAAA,MAAA,cAAA,YAAQ,SAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAA,YAAK,SAAS,YAAY,QAAQ,WAAW,OAAM,MACjE,MAAM,IAAI,UACR,4CAA4C,QAAQ,UAAU,wEAChE;KAEF,IAAI,QAAQ,cAAc,KAAA,GACxB,MAAM,IAAI,UACR,mDAAmD,QAAQ,UAAU,wEACvE;IAEJ;IACA,IAAI,IAAI,SAAS,WAAW,KAAK;;KAC/B,MAAM,IAAI,UACR,oDAAA,qBAAkD,IAAI,SAAS,UAAA,QAAA,uBAAA,KAAA,IAAA,KAAA,IAAA,mBAAM,UAAS,uCAChF;IACF;IACA,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,qDAAqD,QAAQ,SAAS,GAAG,6GAC3E;GAEJ;GAEA,MAAM,IAAI,UAAU,4BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACA;EAC3D;CACF;AACF;;;AC5KA,IAAM,WAAN,cAAuB,SAAS;CAC9B,MAAM,KAAK,SAA2C;EACpD,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,OAAO;EAIT,IAAI,CAAC,QAAQ,SACX,OAAO;EAGT,MAAM,cAAc,KAAK,WAAW,QAAQ,SAAS,MAAM;EAC3D,IAAI,aACF,OAAO;EAOT,IAAI,CAFc;GADI;GAAS;GAAQ;GAAa;GAAa;GAAW;GAAkB;EACjE,CAAC,CAAC,MAAM,UAAU,QAAQ,WAAoC,KAAA,CAE9E,GACX,OAAO;EAIT,IAAI,QAAQ,cAAc,KAAA,KAAa,QAAQ,cAAc,QAAQ,QAAQ,cAAc,IACrF;OAAA,QAAQ,UAAU,SAAS,MAAM,CAAC,QAAQ,UAAU,MAAM,WAAW,GACvE,OAAO,WAAW,QAAQ,UAAU;EAAA;EAIxC,IAAI;GAEF,MAAM,cAAc,KAAK,OACvB,MAAM,KAAK,UAAU,IAAgB,UAAU,QAAQ,WAAW,EAChE,OAAO,EAAE,QAAQ,uEAAuE,EAC1F,CAAC,CACH;GAEA,MAAM,mBAAmB,0BAA0B,WAAW;GAC9D,IAAI,qBAAqB,KAAA,GACvB,MAAM,IAAI,UAAU,wBAAwB,QAAQ,QAAQ,KAAK,kBAAkB;GAErF,IAAI,EAAA,gBAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAC,YAAa,KAChB,MAAM,IAAI,UACR,iBAAiB,QAAQ,QAAQ,2DACnC;GAIF,MAAM,aAAuC,CAAC;GAE9C,IAAI,QAAQ,UAAU,KAAA,GAAW,WAAW,QAAQ,QAAQ;GAC5D,IAAI,QAAQ,SAAS,KAAA,GAAW,WAAW,OAAO,QAAQ;GAC1D,IAAI,QAAQ,cAAc,KAAA,GAAW,WAAW,YAAY,QAAQ;GACpE,IAAI,QAAQ,cAAc,KAAA,GAAW,WAAW,YAAY,QAAQ;GACpE,IAAI,QAAQ,YAAY,KAAA,GAAW,WAAW,UAAU,QAAQ;GAChE,IAAI,QAAQ,mBAAmB,KAAA,GAAW,WAAW,iBAAiB,QAAQ;GAC9E,IAAI,QAAQ,aAAa,KAAA,GAAW,WAAW,WAAW,QAAQ;GAGlE,MAAM,cAAc,KAAK,OACvB,MAAM,KAAK,UAAU,IAAsB,UAAU,QAAQ,WAAW,UAAU,CACpF;GAIA,MAAM,cAAc,0BAA0B,WAAW;GACzD,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,UAAU,0BAA0B,aAAa;GAE7D,IAAI,CAAC,eAAe,OAAO,gBAAgB,YAAY,CAAC,YAAY,IAClE,MAAM,IAAI,UACR,iGAAiG,KAAK,UAAU,WAAW,GAC7H;GAIF,MAAM,oBAAoB,OAAO,aAAsC;IACrE,IAAI;KACF,MAAM,WAAW,KAAK,OACpB,MAAM,KAAK,UAAU,IAAkB,YAAY,YAAY,EAC7D,OAAO,EAAE,QAAQ,QAAQ,EAC3B,CAAC,CACH;KACA,IAAA,aAAA,QAAA,aAAA,KAAA,IAAA,KAAA,IAAI,SAAU,OACZ,OAAO,IAAI,SAAS,MAAM;KAE5B,OAAO,gBAAgB;IACzB,QAAQ;KACN,OAAO,gBAAgB;IACzB;GACF;GAEA,MAAM,kBAAkB,YAAY,YAAY,MAAM,kBAAkB,YAAY,SAAS,IAAI;GACjG,MAAM,kBAAkB,OAAO,YAA6B;IAC1D,IAAI,CAAC,YAAY,WAAW,OAAO;IACnC,IAAI,YAAY,cAAc,YAAY,WAAW,OAAO;IAC5D,OAAO,kBAAkB,YAAY,SAAS;GAChD,EAAA,CAAG;GAGH,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,8BAA8B;GAC/C,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,aAAa,YAAY,SAAS,WAAW,EAAE;GAChE,YAAY,KAAK,eAAe,YAAY,IAAI;GAChD,YAAY,KAAK,EAAE;GAGnB,YAAY,KAAK,kBAAkB;GAEnC,IAAI,QAAQ,UAAU,KAAA,KAAa,YAAY,UAAU,YAAY,OACnE,YAAY,KAAK,cAAc,YAAY,MAAM,OAAO,YAAY,MAAM,EAAE;GAG9E,IAAI,QAAQ,cAAc,KAAA,KAAa,YAAY,cAAc,YAAY,WAC3E,YAAY,KAAK,gBAAgB,gBAAgB,KAAK,iBAAiB;GAGzE,IAAI,QAAQ,YAAY,KAAA,KAAa,YAAY,YAAY,YAAY,SAAS;IAChF,MAAM,UAAU,YAAY,UAAU,SAAS;IAC/C,MAAM,UAAU,YAAY,UAAU,SAAS;IAC/C,YAAY,KAAK,YAAY,QAAQ,KAAK,SAAS;GACrD;GAEA,IAAI,QAAQ,mBAAmB,KAAA,KAAa,YAAY,mBAAmB,YAAY,gBAAgB;IACrG,MAAM,YAAY,YAAY,iBAAiB,cAAc;IAC7D,MAAM,YAAY,YAAY,iBAAiB,cAAc;IAC7D,YAAY,KAAK,mBAAmB,UAAU,KAAK,WAAW;GAChE;GAEA,IAAI,QAAQ,aAAa,KAAA,GAAW;IAClC,MAAM,SAAS,YAAY,WAAW,KAAK,WAAW,YAAY,QAAQ,IAAI;IAC9E,MAAM,SAAS,YAAY,WAAW,KAAK,WAAW,YAAY,QAAQ,IAAI;IAC9E,IAAI,WAAW,QACb,YAAY,KAAK,gBAAgB,OAAO,KAAK,QAAQ;GAEzD;GAEA,IAAI,QAAQ,SAAS,KAAA,GACnB,YAAY,KAAK,qBAAqB;GAGxC,IAAI,QAAQ,cAAc,KAAA,GACxB,YAAY,KAAK,0BAA0B;GAG7C,MAAM,cAAc,KAAK,WAAW,YAAY,YAAY;GAC5D,YAAY,KAAK,oBAAoB,aAAa;GAElD,YAAY,KAAK,EAAE;GACnB,YAAY,KAAK,gBAAgB;GACjC,YAAY,KAAK,0CAA0C,YAAY,GAAG,EAAE;GAC5E,IAAI,YAAY,WACd,YAAY,KAAK,kDAAkD,YAAY,UAAU,EAAE;GAG7F,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,wBAAwB,MAAM,GAAG;GACtD,IAAI,IAAI,UAAU;IAChB,IAAI,IAAI,SAAS,WAAW,OAAO,QAAQ,cAAc,KAAA,GACvD,MAAM,IAAI,UACR,4CAA4C,QAAQ,UAAU,0EAChE;IAEF,IAAI,IAAI,SAAS,WAAW,KAC1B,MAAM,IAAI,UACR,wCAAwC,QAAQ,QAAQ,2DAC1D;IAEF,IAAI,IAAI,SAAS,WAAW,KAAK;;KAC/B,MAAM,IAAI,UACR,kDAAA,qBAAgD,IAAI,SAAS,UAAA,QAAA,uBAAA,KAAA,IAAA,KAAA,IAAA,mBAAM,UAAS,uCAC9E;IACF;GACF;GAEA,MAAM,IAAI,UAAU,0BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACF;EACzD;CACF;AACF;;;ACtMA,IAAM,gBAAN,cAA4B,SAAS;CACnC,MAAM,OAAwB;EAC5B,IAAI;GACF,MAAM,YAAY,KAAK,OACrB,MAAM,KAAK,UAAU,YAA0B,YAAY,EACzD,OAAO,EAAE,QAAQ,qBAAqB,EACxC,CAAC,CACH;GAEA,MAAM,sBAAsD,CAAC;GAE7D,UAAU,SAAS,aAAa;IAC9B,MAAM,WAAW,SAAS,aAAa;IACvC,IAAI,CAAC,oBAAoB,WACvB,oBAAoB,YAAY,CAAC;IAEnC,oBAAoB,SAAS,CAAC,KAAK,QAAQ;GAC7C,CAAC;GAGD,MAAM,cAAc;IAClB;IACA;IACA;GACF;GAGA,YAAY,KACV,GAAG,KAAK,eAAe,oBAAoB,OAAO,CAAC,GAAG;IACpD,QAAQ;IACR;GACF,CAAC,CACH;GAEA,OAAO,YAAY,KAAK,EAAE;EAC5B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GACtC,QAAQ,OAAO,MAAM,4BAA4B,MAAM,GAAG;GAE1D,MAAM,IAAI,UAAU,6BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACC;EAC5D;CACF;CAEA,eACE,WACA,EACE,SAAS,GACT,uBAKQ;EACV,MAAM,SAAmB,CAAC;EAC1B,MAAM,eAAe,IAAI,OAAO,MAAM;EAEtC,KAAK,cAAc,SAAS,CAAC,CAAC,SAAS,aAAa;GAClD,MAAM,EAAE,OAAO;GACf,OAAO,KAAK,GAAG,aAAa,aAAa,SAAS,MAAM,mBAAmB,GAAG,KAAK;GAEnF,MAAM,iBAAiB,oBAAoB;GAC3C,IAAI,gBACF,OAAO,KACL,GAAG,KAAK,eAAe,gBAAgB;IACrC,QAAQ,SAAS;IACjB;GACF,CAAC,CACH;EAEJ,CAAC;EAED,OAAO;CACT;CAEA,cAAsB,WAA2C;EAE/D,MAAM,qBAAqB,OAAO,aAAa,IAAI,WAAW,CAAC,IAAI,CAAC;EACpE,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,GAAG,MAAM;GACnC,MAAM,SAAS,EAAE,MAAM,QAAQ,KAAK,kBAAkB;GACtD,MAAM,SAAS,EAAE,MAAM,QAAQ,KAAK,kBAAkB;GACtD,OAAO,OAAO,cAAc,MAAM;EACpC,CAAC;CACH;AACF;;;ACnFA,IAAM,gBAAN,cAA4B,SAAS;CACnC,MAAM,KAAK,SAAoC;EAC7C,IAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAC5D,OAAO;EAIT,MAAM,aAAa,QAAQ,QAAQ,OAAO,CAAC,MAAM,GAAG,SAAS,MAAM,CAAC,GAAG,MAAM,WAAW,CAAC;EACzF,IAAI,WAAW,SAAS,GACtB,OAAO,uDAAuD,WAAW,KAAK,IAAI,EAAE;EAGtF,MAAM,cAAwB,CAAC;EAC/B,MAAM,WAAqB,CAAC;EAC5B,MAAM,SAAmB,CAAC;EAC1B,MAAM,aAAuB,CAAC;EAG9B,YAAY,KAAK,aAAa,QAAQ,OAAO,SAAS;EAGtD,KAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,QAAQ,GAAG;GAC3C,YAAY,KAAK,WAAW,IAAI,EAAE,MAAM,QAAQ,OAAO,QAAQ,OAAO,IAAI;GAE1E,IAAI;IAEF,MAAM,OAAO,KAAK,OAChB,MAAM,KAAK,UAAU,IAAgB,UAAU,UAAU,EACvD,OAAO,EACL,QAAQ,oFACV,EACF,CAAC,CACH;IAGA,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,KAAK,IAAI;KACjD,OAAO,KAAK,MAAM;KAClB,YAAY,KAAK,wEAAwE,OAAO,GAAG;KACnG;IACF;IAEA,WAAW,KAAK,MAAM;IAGtB,MAAM,eAAe,OAAO,YAA6B;KACvD,IAAI,CAAC,KAAK,WAAW,OAAO;KAC5B,IAAI;MACF,MAAM,WAAW,KAAK,OACpB,MAAM,KAAK,UAAU,IAAkB,YAAY,KAAK,aAAa,EACnE,OAAO,EAAE,QAAQ,WAAW,EAC9B,CAAC,CACH;MACA,IAAA,aAAA,QAAA,aAAA,KAAA,IAAA,KAAA,IAAI,SAAU,OACZ,OAAO,IAAI,SAAS,MAAM,mBAAmB,KAAK,UAAU;MAE9D,OAAO;KACT,SAAS,KAAc;MACrB,QAAQ,OAAO,MAAM,yCAAyC,OAAO,IAAI,IAAI,GAAG;MAChF,OAAO;KACT;IACF,EAAA,CAAG;IAGH,YAAY,KAAK,cAAc,KAAK,MAAM,EAAE;IAC5C,YAAY,KAAK,aAAa,cAAc;IAG5C,IAAI,KAAK,SAAS;KAChB,MAAM,SAAS,KAAK,iBAAiB,cAAc;KACnD,YAAY,KAAK,WAAW,QAAQ;KAEpC,IAAI,KAAK,UAAU;MACjB,MAAM,UAAU,KAAK,WAAW,KAAK,QAAQ;MAC7C,YAAY,KAAK,QAAQ,SAAS;KACpC;IACF;IAGA,MAAM,cAAc,KAAK,WAAW,KAAK,YAAY;IACrD,MAAM,cAAc,KAAK,WAAW,KAAK,YAAY;IACrD,YAAY,KAAK,YAAY,aAAa;IAC1C,YAAY,KAAK,YAAY,aAAa;IAG1C,YAAY,KAAK,SAAS;IAG1B,IAAI,KAAK,MACP,YAAY,KAAK,KAAK,IAAI;SAE1B,YAAY,KAAK,4BAA4B;IAI/C,YAAY,KAAK,SAAS;GAC5B,SAAS,OAAgB;;IACvB,QAAQ,OAAO,MAAM,sBAAsB,OAAO,IAAI,MAAM,GAAG;IAC/D,MAAM,MAAM;IACZ,MAAA,gBAAI,IAAI,cAAA,QAAA,kBAAA,KAAA,IAAA,KAAA,IAAA,cAAU,YAAW,KAAK;KAChC,SAAS,KAAK,MAAM;KACpB,YAAY,KAAK,iBAAiB,OAAO,eAAe;IAC1D,OAAO;KACL,OAAO,KAAK,MAAM;KAClB,YAAY,KAAK,uBAAuB,IAAI,WAAW,gBAAgB,GAAG;IAC5E;GACF;EACF;EAGA,YAAY,KAAK,WAAW;EAC5B,YAAY,KAAK,0BAA0B,QAAQ,QAAQ;EAC3D,YAAY,KAAK,2BAA2B,WAAW,QAAQ;EAE/D,IAAI,SAAS,SAAS,GAAG;GACvB,YAAY,KAAK,oBAAoB,SAAS,QAAQ;GACtD,YAAY,KAAK,kBAAkB,SAAS,KAAK,IAAI,GAAG;EAC1D;EAEA,IAAI,OAAO,SAAS,GAAG;GACrB,YAAY,KAAK,uBAAuB,OAAO,QAAQ;GACvD,YAAY,KAAK,oBAAoB,OAAO,KAAK,IAAI,GAAG;EAC1D;EAEA,OAAO,YAAY,KAAK,IAAI;CAC9B;AACF;;;AC7HA,IAAM,WAAN,cAAuB,SAAS;CAC9B,MAAM,KAAK,QAAiC;EAC1C,MAAM,kBAAkB,KAAK,WAAW,QAAQ,MAAM;EACtD,IAAI,iBACF,OAAO;EAGT,IAAI;GAEF,MAAM,OAAO,KAAK,OAChB,MAAM,KAAK,UAAU,IAAgB,UAAU,UAAU,EACvD,OAAO,EACL,QAAQ,oFACV,EACF,CAAC,CACH;GAEA,MAAM,gBAAgB,0BAA0B,IAAI;GACpD,IAAI,kBAAkB,KAAA,GACpB,MAAM,IAAI,UAAU,wBAAwB,OAAO,KAAK,eAAe;GAEzE,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,KAAK,IAC7C,MAAM,IAAI,UACR,wBAAwB,OAAO,4EAA4E,KAAK,UAAU,IAAI,GAChI;GAIF,MAAM,eAAe,OAAO,YAA6B;IACvD,IAAI,CAAC,KAAK,WAAW,OAAO;IAC5B,IAAI;KACF,MAAM,WAAW,KAAK,OACpB,MAAM,KAAK,UAAU,IAAkB,YAAY,KAAK,aAAa,EACnE,OAAO,EAAE,QAAQ,WAAW,EAC9B,CAAC,CACH;KACA,IAAA,aAAA,QAAA,aAAA,KAAA,IAAA,KAAA,IAAI,SAAU,OACZ,OAAO,IAAI,SAAS,MAAM,mBAAmB,KAAK,UAAU;KAE9D,OAAO;IACT,SAAS,KAAc;KACrB,QAAQ,OAAO,MAAM,iCAAiC,IAAI,GAAG;KAC7D,OAAO;IACT;GACF,EAAA,CAAG;GAGH,MAAM,cAAwB,CAAC;GAG/B,YAAY,KAAK,YAAY,KAAK,MAAM,EAAE;GAC1C,YAAY,KAAK,YAAY,KAAK,IAAI;GACtC,YAAY,KAAK,aAAa,cAAc;GAG5C,IAAI,KAAK,SAAS;IAChB,MAAM,SAAS,KAAK,iBAAiB,cAAc;IACnD,YAAY,KAAK,WAAW,QAAQ;IAEpC,IAAI,KAAK,UAAU;KACjB,MAAM,UAAU,KAAK,WAAW,KAAK,QAAQ;KAC7C,YAAY,KAAK,QAAQ,SAAS;IACpC;GACF;GAGA,MAAM,cAAc,KAAK,WAAW,KAAK,YAAY;GACrD,MAAM,cAAc,KAAK,WAAW,KAAK,YAAY;GACrD,YAAY,KAAK,YAAY,aAAa;GAC1C,YAAY,KAAK,YAAY,aAAa;GAG1C,YAAY,KAAK,SAAS;GAG1B,IAAI,KAAK,MACP,YAAY,KAAK,KAAK,IAAI;QAE1B,YAAY,KAAK,4BAA4B;GAI/C,YAAY,KAAK,SAAS;GAC1B,YAAY,KAAK,mBAAmB;GACpC,YAAY,KAAK,2EAA2E,KAAK,UAAU,EAAE;GAC7G,YAAY,KAAK,qEAAmE;GAEpF,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,uBAAuB,MAAM,GAAG;GACrD,MAAA,gBAAI,IAAI,cAAA,QAAA,kBAAA,KAAA,IAAA,KAAA,IAAA,cAAU,YAAW,KAC3B,MAAM,IAAI,UACR,iBAAiB,OAAO,oLAC1B;GAGF,MAAM,IAAI,UACR,wBAAwB,OAAO,KAFjB,iBAAiB,QAAQ,MAAM,UAAU,gBAEX,wFAC9C;EACF;CACF;AACF;;;ACpGA,IAAM,eAAN,cAA2B,SAAS;CAClC,MAAM,KAAK,YAAqC;EAC9C,MAAM,kBAAkB,KAAK,WAAW,YAAY,UAAU;EAC9D,IAAI,iBACF,OAAO;EAGT,IAAI;GAEF,MAAM,WAAW,KAAK,OACpB,MAAM,KAAK,UAAU,IAAkB,YAAY,cAAc,EAC/D,OAAO,EAAE,QAAQ,qBAAqB,EACxC,CAAC,CACH;GAEA,MAAM,oBAAoB,0BAA0B,QAAQ;GAC5D,IAAI,sBAAsB,KAAA,GACxB,MAAM,IAAI,UAAU,4BAA4B,WAAW,KAAK,mBAAmB;GAErF,IAAI,CAAC,YAAY,OAAO,aAAa,YAAY,CAAC,SAAS,IACzD,MAAM,IAAI,UACR,4BAA4B,WAAW,gFAAgF,KAAK,UAAU,QAAQ,GAChJ;GAIF,MAAM,QAAQ,KAAK,OACjB,MAAM,KAAK,UAAU,IAA2B,YAAY,WAAW,SAAS,EAC9E,OAAO,EAAE,QAAQ,+CAA+C,EAClE,CAAC,CACH;GAEA,MAAM,iBAAiB,0BAA0B,KAAK;GACtD,IAAI,mBAAmB,KAAA,GACrB,MAAM,IAAI,UAAU,sCAAsC,WAAW,KAAK,gBAAgB;GAE5F,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,MAAM,IAAI,UACR,sCAAsC,WAAW,+DAA+D,KAAK,UAAU,KAAK,GACtI;GAGF,IAAI,CAAC,MAAM,SAAS,CAAC,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,WAAW,GACxE,OAAO,aAAa,SAAS,MAAM,mBAAmB,SAAS,GAAG;GAIpE,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,gBAAgB,SAAS,MAAM,mBAAmB,SAAS,GAAG,GAAG;GAClF,YAAY,KAAK,YAAY,MAAM,MAAM,OAAO,UAAU;GAC1D,YAAY,KAAK,mDAAmD,SAAS,MAAM,0BAA0B;GAG7G,IAAI,MAAM,MAAM,SAAS,GAAG;IAC1B,MAAM,UAAU,MAAM,MAAM,KAAK,SAAS,KAAK,EAAE;IACjD,YAAY,KAAK,oBAAoB,MAAM,MAAM,OAAO,uBAAuB;IAC/E,YAAY,KAAK,2BAA2B,KAAK,UAAU,OAAO,EAAE,GAAG;GACzE;GAKA,CAFqB,GAAG,MAAM,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,eAAe,EAAE,YAE7D,CAAC,CAAC,SAAS,SAAS;IAC5B,MAAM,cAAc,KAAK,WAAW,KAAK,YAAY;IAGrD,IAAI,KAAK,SAAS;KAChB,MAAM,iBAAiB,KAAK,iBAAiB,MAAM;KACnD,YAAY,KAAK,KAAK,eAAe,UAAU,KAAK,MAAM,eAAe,KAAK,GAAG,GAAG;IACtF,OACE,YAAY,KAAK,YAAY,KAAK,MAAM,eAAe,KAAK,GAAG,GAAG;IAGpE,YAAY,KAAK,cAAc,aAAa;IAC5C,YAAY,KAAK,2CAA2C,KAAK,GAAG,EAAE;IACtE,YAAY,KAAK,EAAE;GACrB,CAAC;GAED,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;;GACvB,IAAI,iBAAiB,WAAW,MAAM;GAEtC,MAAM,MAAM;GACZ,QAAQ,OAAO,MAAM,2BAA2B,MAAM,GAAG;GACzD,MAAA,gBAAI,IAAI,cAAA,QAAA,kBAAA,KAAA,IAAA,KAAA,IAAA,cAAU,YAAW,KAC3B,MAAM,IAAI,UACR,qBAAqB,WAAW,+MAClC;GAGF,MAAM,IAAI,UACR,4BAA4B,WAAW,KAFzB,iBAAiB,QAAQ,MAAM,UAAU,gBAEH,kIACtD;EACF;CACF;AACF;;;AC/FA,IAAM,cAAN,cAA0B,SAAS;CACjC,MAAM,KAAK,OAAgC;EACzC,IAAI,CAAC,OACH,OAAO;EAGT,IAAI;GAEF,MAAM,gBAAgB,KAAK,OACzB,MAAM,KAAK,UAAU,IAAkB,WAAW,EAChD,OAAO;IACL;IACA,QAAQ;GACV,EACF,CAAC,CACH;GAGA,IAAI,CAAC,iBAAiB,OAAO,kBAAkB,UAC7C,OAAO;GAIT,IAAI,CAAC,cAAc,SAAS,CAAC,MAAM,QAAQ,cAAc,KAAK,KAAK,cAAc,MAAM,WAAW,GAChG,OAAO,mCAAmC,MAAM;GAIlD,MAAM,UAAU,KAAK,OACnB,MAAM,KAAK,UAAU,YAA0B,YAAY,EACzD,OAAO,EACL,QAAQ,WACV,EACF,CAAC,CACH;GAGA,MAAM,YAAoC,CAAC;GAC3C,QAAQ,SAAS,WAAW;IAC1B,UAAU,OAAO,MAAM,OAAO;GAChC,CAAC;GAGD,MAAM,cAAwB,CAAC;GAC/B,YAAY,KAAK,SAAS,cAAc,MAAM,OAAO,0BAA0B,MAAM,IAAI;GACzF,YAAY,KAAK,sEAAsE;GAGvF,IAAI,cAAc,MAAM,SAAS,GAAG;IAClC,MAAM,UAAU,cAAc,MAAM,KAAK,SAAS,KAAK,EAAE;IACzD,YAAY,KAAK,oBAAoB,cAAc,MAAM,OAAO,uBAAuB;IACvF,YAAY,KAAK,2BAA2B,KAAK,UAAU,OAAO,EAAE,GAAG;GACzE;GAEA,cAAc,MAAM,SAAS,SAAS;IACpC,MAAM,gBAAgB,UAAU,KAAK,aAAa,OAAO;IACzD,MAAM,aAAa,KAAK,aAAa;IACrC,MAAM,cAAc,KAAK,WAAW,KAAK,YAAY;IAErD,YAAY,KAAK,YAAY,KAAK,MAAM,eAAe,KAAK,GAAG,GAAG;IAClE,YAAY,KAAK,gBAAgB,cAAc,mBAAmB,WAAW,GAAG;IAChF,YAAY,KAAK,cAAc,aAAa;IAG5C,IAAI,KAAK,MAAM;KACb,MAAM,UAAU,KAAK,KAAK,UAAU,GAAG,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG,KAAK,KAAK,KAAK,SAAS,MAAM,QAAQ;KACpG,YAAY,KAAK,cAAc,SAAS;IAC1C;IAGA,YAAY,KAAK,2CAA2C,KAAK,GAAG,EAAE;IACtE,YAAY,KAAK,uDAAuD,WAAW,EAAE;IAErF,YAAY,KAAK,EAAE;GACrB,CAAC;GAED,OAAO,YAAY,KAAK,IAAI;EAC9B,SAAS,OAAgB;GACvB,IAAI,iBAAiB,WAAW,MAAM;GACtC,QAAQ,OAAO,MAAM,0BAA0B,MAAM,GAAG;GAExD,MAAM,IAAI,UAAU,2BADJ,iBAAiB,QAAQ,MAAM,UAAU,iBACD;EAC1D;CACF;AACF;;;ACvEA,IAAM,kBAAN,MAAsB;CACpB;CACA;CAEA,YAAY,EAAE,OAAO,aAAa,OAAO,OAAO,SAAgC;EAC9E,KAAK,UAAU,UAAU,KAAK,GAAG;EACjC,KAAK,QAAQ;CACf;CAEA,MAAM,mBAAiD;EACrD,IAAI;GACF,MAAM,WAAkC,MAAM,MAAM,IAAI,GAAG,KAAK,QAAQ,QAAQ,EAAE,SAAS,IAAM,CAAC;GAClG,IAAI,SAAS,WAAW,OAAO,SAAS,SAAS,uBAC/C,OAAO,MAAM,IAAa;GAE5B,OAAO,qBAAK,IAAI,MAAM,sCAAsC,CAAC;EAC/D,SAAS,OAAgB;GACvB,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,MAAM,YAAyB,MAAc,UAA0B,CAAC,GAAgC;EACtG,MAAM,YAAY,OAAO,MAAc,QAA2B;GAEhE,MAAM,YAAW,MADI,KAAK,IAA0B,MAAM,KAAK,oBAAoB,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,EAAA,CACxF,MACrB,QAAQ;IACP,MAAM;GACR,IACC,SAAS,IACZ;GACA,IAAI,CAAC,MAAM,QAAQ,SAAS,KAAK,GAC/B,MAAM,IAAI,MAAM,wDAAwD,MAAM;GAEhF,MAAM,WAAW,CAAC,GAAG,KAAK,GAAG,SAAS,KAAK;GAC3C,OAAO,SAAS,WAAW,UAAU,OAAO,GAAG,QAAQ,IAAI;EAC7D;EAEA,IAAI;GACF,OAAO,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC;EACrC,SAAS,OAAgB;GACvB,QAAQ,OAAO,MAAM,iCAAiC,KAAK,IAAI,MAAM,GAAG;GACxE,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,MAAM,IAAiB,MAAc,UAA0B,CAAC,GAA8B;EAC5F,IAAI;GACF,MAAM,EAAE,SAA2B,MAAM,MAAM,IAAI,GAAG,KAAK,UAAU,QAAQ;IAC3E,QAAQ,KAAK,eAAe,OAAO,CAAC,CAAC;IACrC,SAAS;GACX,CAAC;GACD,OAAO,MAAM,IAAI;EACnB,SAAS,OAAgB;GACvB,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,MAAM,KAAkB,MAAc,MAAe,UAA0B,CAAC,GAA8B;EAC5G,IAAI;GACF,MAAM,EAAE,SAA2B,MAAM,MAAM,KAAK,GAAG,KAAK,UAAU,QAAQ,MAAM;IAClF,QAAQ,KAAK,eAAe,OAAO,CAAC,CAAC;IACrC,SAAS;GACX,CAAC;GACD,OAAO,MAAM,IAAI;EACnB,SAAS,OAAgB;GACvB,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,MAAM,OAAoB,MAAc,UAA0B,CAAC,GAA8B;EAC/F,IAAI;GACF,MAAM,EAAE,SAA2B,MAAM,MAAM,OAAO,GAAG,KAAK,UAAU,QAAQ;IAC9E,QAAQ,KAAK,eAAe,OAAO,CAAC,CAAC;IACrC,SAAS;GACX,CAAC;GACD,OAAO,MAAM,IAAI;EACnB,SAAS,OAAgB;GACvB,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,MAAM,IAAiB,MAAc,MAAe,UAA0B,CAAC,GAA8B;EAC3G,IAAI;GACF,MAAM,EAAE,SAA2B,MAAM,MAAM,IAAI,GAAG,KAAK,UAAU,QAAQ,MAAM;IACjF,QAAQ,KAAK,eAAe,OAAO,CAAC,CAAC;IACrC,SAAS;GACX,CAAC;GACD,OAAO,MAAM,IAAI;EACnB,SAAS,OAAgB;GACvB,OAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACvE;CACF;CAEA,eAAuB,UAA0B,CAAC,GAAmB;EACnE,OAAO,KAAK,oBACV,EACE,OAAO,EAAE,OAAO,KAAK,MAAM,EAC7B,GACA,OACF;CACF;CAEA,oBAA4B,UAA0B,UAA0C;EAC9F,OAAO;GACL,OAAO;IACL,GAAI,SAAS,SAAS,CAAC;IACvB,GAAI,SAAS,SAAS,CAAC;GACzB;GACA,GAAG,KAAK,OAAO,UAAU,OAAO;GAChC,GAAG,KAAK,OAAO,UAAU,OAAO;EAClC;CACF;CAEA,OAAe,KAA8B,KAAsC;EACjF,MAAM,SAAS,EAAE,GAAG,IAAI;EACxB,OAAO,OAAO;EACd,OAAO;CACT;AACF;;;ACjHA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA,YAA6B;CAC7B;CAcA,YAAY,QAA4B;EACtC,KAAK,SAAS;EACd,KAAK,YAAY,IAAI,gBAAgB;GACnC,MAAM,OAAO;GACb,MAAM,OAAO;GACb,OAAO,OAAO;EAChB,CAAC;EAED,KAAK,QAAQ;GACX,eAAe,IAAI,cAAc,KAAK,SAAS;GAC/C,aAAa,IAAI,YAAY,KAAK,SAAS;GAC3C,cAAc,IAAI,aAAa,KAAK,SAAS;GAC7C,UAAU,IAAI,SAAS,KAAK,SAAS;GACrC,eAAe,IAAI,cAAc,KAAK,SAAS;GAC/C,YAAY,IAAI,WAAW,KAAK,SAAS;GACzC,cAAc,IAAI,aAAa,KAAK,SAAS;GAC7C,UAAU,IAAI,SAAS,KAAK,SAAS;GACrC,YAAY,IAAI,WAAW,KAAK,SAAS;GACzC,YAAY,IAAI,WAAW,KAAK,SAAS;GACzC,cAAc,IAAI,aAAa,KAAK,SAAS;EAC/C;CACF;CAEA,MAAM,kBAAiC;EACrC,IAAI,KAAK,WAAW;GAClB,MAAM,SAAS,MAAM,KAAK,UAAU,iBAAiB;GACrD,IAAI,OAAO,QAAQ,MAAM,GAAG;GAC5B,KAAK,YAAY;EACnB;EAEA,MAAM,YAAY,MAAM,KAAK,UAAU,iBAAiB;EACxD,IAAI,OAAO,QAAQ,SAAS,GAAG;GAC7B,KAAK,YAAY;GACjB,QAAQ,OAAO,MAAM,0BAA0B,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,GAAG;GACvF;EACF;EAGA,IAAI,KAAK,OAAO,SAAS;GACvB,QAAQ,OAAO,MAAM,6CAA6C;GAClE,MAAM,cAAc,MAAM,KAAK,OAAO,QAAQ,MAAM;GACpD,IAAI,OAAO,OAAO,WAAW,GAAG;IAC9B,MAAM,QAAQ,YAAY,MACvB,MAAM,SACD,IACR;IACA,MAAM,IAAI,MAAM,mBAAmB,MAAM,KAAK,KAAK,MAAM,SAAS;GACpE;GACA,MAAM,iBAAiB,MAAM,KAAK,UAAU,iBAAiB;GAC7D,IAAI,OAAO,QAAQ,cAAc,GAAG;IAClC,KAAK,YAAY;IACjB,QAAQ,OAAO,MAAM,sCAAsC,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,GAAG;IACnG;GACF;EACF;EAEA,MAAM,IAAI,MACR,8BAA8B,KAAK,OAAO,KAAK,GAAG,KAAK,OAAO,KAAK,0DAErE;CACF;CAGA,MAAM,gBAAiC;EACrC,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,cAAc,KAAK;CAC7C;CAEA,MAAM,YAAY,OAAgC;EAChD,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,YAAY,KAAK,KAAK;CAChD;CAEA,MAAM,aAAa,YAAqC;EACtD,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,aAAa,KAAK,UAAU;CACtD;CAEA,MAAM,SAAS,QAAiC;EAC9C,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,SAAS,KAAK,MAAM;CAC9C;CAEA,MAAM,cAAc,SAAoC;EACtD,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,cAAc,KAAK,OAAO;CACpD;CAEA,MAAM,WAAW,QAOG;EAClB,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM;CAChD;CAEA,MAAM,aAAa,QAA4E;EAC7F,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,aAAa,KAAK,MAAM;CAClD;CAEA,MAAM,SAAS,QASK;EAClB,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,SAAS,KAAK,MAAM;CAC9C;CAEA,MAAM,WAAW,QAIG;EAClB,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM;CAChD;CAEA,MAAM,WAAW,QAA6E;EAC5F,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM;CAChD;CAEA,MAAM,aAAa,QAIC;EAClB,MAAM,KAAK,gBAAgB;EAC3B,OAAO,MAAM,KAAK,MAAM,aAAa,KAAK,MAAM;CAClD;CAEA,MAAM,OAAwB;;EAC5B,MAAM,KAAK,gBAAgB;EAC3B,MAAM,mBAAA,uBAAiB,KAAK,OAAO,aAAA,QAAA,yBAAA,KAAA,IAAA,KAAA,IAAA,qBAAS,kBAAkB,KAC1D,6KAEA;EAGJ,QAAO,MADc,KAAK,UAAU,KAA8B,kBAAkB,EAAE,QAAQ,QAAQ,CAAC,EAAA,CACzF,MACX,UAAU;GACT,MAAM,MAAM,MAAM,WAAW,OAAO,KAAK;GAGzC,IAAI,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,eAAe,KAAK,IAAI,SAAS,iBAAiB,GACxF,OACE,yKAC2F;GAG/F,OAAO,gBAAgB,MAAM;EAC/B,SACM,+BAA+B,gBACvC;CACF;AACF;AAEA,SAAgB,wBAAwB,QAAiD;CACvF,OAAO,IAAI,oBAAoB,MAAM;AACvC;;;;;;;;ACtMA,MAAM,mBAAmB,OAAU,OAAqC;CACtE,IAAI;EACF,OAAO,MAAM,GAAG;CAClB,SAAS,GAAG;EACV,IAAI,aAAa,WAAW,MAAM,IAAI,UAAU,EAAE,OAAO;EACzD,MAAM;CACR;AACF;AAGA,MAAM,YAAY,QADC,cAAc,OAAO,KAAK,GACV,CAAC;AAEpC,MAAM,UADc,KAAK,MAAM,aAAa,KAAK,WAAW,MAAM,cAAc,GAAG,OAAO,CAChE,CAAC,CAAC;AAU5B,SAAgB,oBAAoB,SAAkF;CACpH,QAAQ,OAAO,MAAM,6CAA6C;CAGlE,MAAM,UAAU,wBAAwB;EAAE,MAAM,QAAQ;EAAM,MAAM,QAAQ;EAAM,OAAO,QAAQ;CAAM,CAAC;CAGxG,MAAM,SAAS,IAAI,QAAQ;EACzB,MAAM;EACN,SAAS;EACT,QAAQ;GACN,SAAS;GACT,MAAM;GACN,QAAQ;GACR,SAAS,KAAK,UAAU;IACtB,QAAQ;IACR,SAAS;IACT,SAAS;IACT,YAAY,QAAQ;IACpB,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GACpC,CAAC;EACH;CACF,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO,CAAC,CAAC;EACvB,eAAe,uBAAuB,QAAQ,cAAc,CAAC;CAC/D,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO,EACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,wBAAwB,EACrD,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,YAAY,KAAK,KAAK,CAAC;CAC3E,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO,EACnB,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS,4BAA4B,EAC/D,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,aAAa,KAAK,WAAW,CAAC;CAClF,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO,EACnB,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,wBAAwB,EACvD,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,SAAS,KAAK,OAAO,CAAC;CAC1E,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO,EACnB,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,2BAA2B,EACpE,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,cAAc,KAAK,QAAQ,CAAC;CAChF,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO;GACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,YAAY;GAClD,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0BAA0B;GAC/D,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,sBAAsB;GAChE,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uBAAuB;GACjE,SAAS,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,6BAA6B;GACtE,gBAAgB,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,+BAA+B;EAChF,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,WAAW,IAAI,CAAC;CACpE,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO;GACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,gBAAgB;GAC3C,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,uBAAuB;EACnE,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,aAAa,IAAI,CAAC;CACtE,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO;GACnB,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,wBAAwB;GACrD,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gBAAgB;GACtD,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,8BAA8B;GACnE,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0BAA0B;GACpE,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB;GAClE,SAAS,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,6BAA6B;GACtE,gBAAgB,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,2BAA2B;GAC3E,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;EAC3E,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,SAAS,IAAI,CAAC;CAClE,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO;GACnB,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,0BAA0B;GACzD,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,kBAAkB;GACxD,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,sBAAsB;EAClE,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,WAAW,IAAI,CAAC;CACpE,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO;GACnB,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,0BAA0B;GACvD,SAAS,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mBAAmB;EAC9D,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,WAAW,IAAI,CAAC;CACpE,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO;GACnB,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,4BAA4B;GAC3D,SAAS,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mBAAmB;GAC5D,OAAO,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,0CAA0C;EACnF,CAAC;EACD,UAAU,SAAS,uBAAuB,QAAQ,aAAa,IAAI,CAAC;CACtE,CAAC;CAGD,OAAO,QAAQ;EACb,MAAM;EACN,aAAa;EACb,YAAY,EAAE,OAAO,CAAC,CAAC;EACvB,eAAe,uBAAuB,QAAQ,KAAK,CAAC;CACtD,CAAC;CAED,QAAQ,OAAO,MAAM,kDAAkD;CACvE,OAAO;EAAE;EAAQ;CAAQ;AAC3B;AAEA,eAAsB,mBAAmB,SAA8C;CACrF,MAAM,EAAE,WAAW,oBAAoB,OAAO;CAE9C,QAAQ,OAAO,MAAM,4BAA4B,QAAQ,KAAK,GAAG,QAAQ,KAAK,GAAG;CAEjF,MAAM,OAAO,QAAQ,YAAY;CACjC,MAAM,WAAW,QAAQ,YAAY;CAErC,MAAM,OAAO,MAAM;EACjB,eAAe;EACf,YAAY;GACV;GACU;EACZ;CACF,CAAC;CAED,QAAQ,OAAO,MAAM,4CAA4C,OAAO,SAAS,GAAG;AACtF;;;ACnMA,MAAM,EAAE,WAAW,UAAU,YAAY,YAAY,kBADlC,UAC6D;AAEhF,MAAM,aAAa,cAAc;AAKjC,MAAM,eACJ,QAAQ,IAAI,gBAAgB,KAAA,KAAa,QAAQ,IAAI,gBAAgB,KAAK,QAAQ,IAAI,cAAc,KAAA;AACtG,MAAM,eACJ,QAAQ,IAAI,gBAAgB,KAAA,KAAa,QAAQ,IAAI,gBAAgB,KACjE,SAAS,QAAQ,IAAI,aAAa,EAAE,IACpC,KAAA;AACN,MAAM,eAAe,iBAAiB,KAAA,KAAa,iBAAiB,KAAA;AAEpE,MAAM,sBAA8B,OAAO,OAAO,WAAW,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE;AAE5F,MAAM,qBAAqB,cAAc,YAAY;AACrD,MAAM,eAAe,QAAQ,IAAI;AACjC,MAAM,aAAa;CAAE;CAAc;CAAe,cAAc,OAAO,YAAY;AAAE;AAIrF,IAAI,qBAAqB,UAAU,GAAG;CACpC,QAAQ,OAAO,MACb,8GACF;CACA,QAAQ,KAAK,CAAC;AAChB;AAMA,MAAM,iCAAyC;CAC7C,MAAM,YAAY,KAAK,KAAK,YAAY,YAAY;CACpD,IAAI;EACF,MAAM,QAAQ,GAAG,aAAa,WAAW,OAAO,CAAC,CAAC,KAAK;EACvD,IAAI,OAAO,OAAO;CACpB,QAAQ,CAER;CACA,MAAM,QAAQ,cAAc;CAC5B,IAAI;EACF,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;EAC5C,GAAG,cAAc,WAAW,KAAK;CACnC,QAAQ,CAER;CACA,OAAO;AACT;AAIA,MAAM,kBAAkB,mBAAmB,UAAU;AACrD,IAAI,gBAAgB,gBAClB,QAAQ,OAAO,MACb,mMAEF;AAEF,MAAM,qBAA6B;CACjC,QAAQ,gBAAgB,QAAxB;EACE,KAAK,YACH,OAAO,sBAAsB,cAAc;EAC7C,KAAK,gBACH,OAAO,gBAAgB,cAAc;EACvC,KAAK,sBACH,OAAO,cAAc;EACvB,KAAK,WACH,OAAO,yBAAyB;CACpC;AACF,EAAA,CAAG;AAEH,eAAe,kBAA+F;CAC5G,IAAI,cAAc;EAEhB,MAAM,OAAO,gBAAgB;EAC7B,MAAM,OAAO,gBAAA;EACb,QAAQ,OAAO,MAAM,0CAA0C,KAAK,GAAG,KAAK,GAAG;EAC/E,OAAO;GAAE;GAAM;GAAM,SAAS,KAAA;EAAU;CAC1C;CAGA,MAAM,UAAU,IAAI,cAAc;EAChC;EACA,SAAS;EACT,UAAU;EACV,YAAY,WAAW,YAAY;EACnC,SAAA;CACF,CAAC;CAKD,CAAA,MADyB,QAAQ,YAAY,EAAA,CAClC,MACR,QAAQ,QAAQ,OAAO,MAAM,oCAAoC,IAAI,QAAQ,GAAG,IAChF,MAAM,QAAQ,OAAO,MAAM,yBAAyB,EAAE,GAAG,CAC5D;CAIA,QAAa,MAAM,CAAC,CAAC,MAAM,WAAW;EACpC,OAAO,MACJ,QAAQ;GACP,QAAQ,OAAO,MAAM,qCAAqC,IAAI,QAAQ,GAAG;GACzE,QAAQ,OAAO,MAAM,wDAAwD;EAC/E,SACM;GACJ,QAAQ,OAAO,MAAM,uCAAuC;EAC9D,CACF;CACF,CAAC;CAGD,MAAM,UAAU,YAAY;EAC1B,MAAM,QAAQ,KAAK;EACnB,QAAQ,KAAK,CAAC;CAChB;CACA,QAAQ,GAAG,gBAAgB,KAAK,QAAQ,CAAC;CACzC,QAAQ,GAAG,iBAAiB,KAAK,QAAQ,CAAC;CAE1C,OAAO;EAAE,MAAM,QAAQ,QAAQ;EAAG,MAAM,QAAQ,QAAQ;EAAG;CAAQ;AACrE;AAGA,eAAe,OAAsB;CACnC,MAAM,EAAE,MAAM,MAAM,YAAY,MAAM,gBAAgB;CAEtD,IAAI,YAAY;EACd,QAAQ,OAAO,MAAM,gDAAgD;EACrE,MAAM,mBAAmB;GACvB;GACA;GACA,OAAO;GACP;GACA,UAAU;EACZ,CAAC;CACH,OAAO;EACL,QAAQ,OAAO,MAAM,oCAAoC;EACzD,MAAM,iBAAiB,MAAM,MAAM,aAAa,OAAO;CACzD;AACF;AAEA,KAAK,CAAC,CAAC,OAAO,UAAU;CACtB,QAAQ,OAAO,MAAM,+BAA+B,MAAM,GAAG;CAC7D,QAAQ,KAAK,CAAC;AAChB,CAAC;AAED,eAAe,iBAAiB,MAAc,MAAc,OAAe,SAAwC;CACjH,MAAM,UAAU,wBAAwB;EAAE;EAAM;EAAM;EAAO;CAAQ,CAAC;CAEtE,MAAM,SAAS,IAAI,OACjB;EACE,MAAM;EACN,SAAA;CACF,GACA,EACE,cAAc;EACZ,WAAW,CAAC;EACZ,OAAO,CAAC;EACR,SAAS,CAAC;CACZ,EACF,CACF;CAGA,OAAO,kBAAkB,8BAA8B;EACrD,OAAO,EACL,OAAO;GACL;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY,CAAC;IACf;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY,EACV,OAAO;MAAE,MAAM;MAAU,aAAa;KAAe,EACvD;KACA,UAAU,CAAC,OAAO;IACpB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY,EACV,aAAa;MAAE,MAAM;MAAU,aAAa;KAA6B,EAC3E;KACA,UAAU,CAAC,aAAa;IAC1B;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY,EACV,SAAS;MAAE,MAAM;MAAU,aAAa;KAAyB,EACnE;KACA,UAAU,CAAC,SAAS;IACtB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY,EACV,UAAU;MAAE,MAAM;MAAS,OAAO,EAAE,MAAM,SAAS;MAAG,aAAa;KAA4B,EACjG;KACA,UAAU,CAAC,UAAU;IACvB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY;MACV,OAAO;OAAE,MAAM;OAAU,aAAa;MAAa;MACnD,MAAM;OAAE,MAAM;OAAU,aAAa;MAA2B;MAChE,WAAW;OAAE,MAAM;OAAU,aAAa;MAAuB;MACjE,WAAW;OAAE,MAAM;OAAU,aAAa;MAAwB;MAClE,SAAS;OAAE,MAAM;OAAW,aAAa;MAA8B;MACvE,gBAAgB;OAAE,MAAM;OAAU,aAAa;MAAgC;KACjF;IACF;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY;MACV,OAAO;OAAE,MAAM;OAAU,aAAa;MAAiB;MACvD,WAAW;OAAE,MAAM;OAAU,aAAa;MAAwB;KACpE;KACA,UAAU,CAAC,OAAO;IACpB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY;MACV,SAAS;OAAE,MAAM;OAAU,aAAa;MAAyB;MACjE,OAAO;OAAE,MAAM;OAAU,aAAa;MAAiB;MACvD,MAAM;OAAE,MAAM;OAAU,aAAa;MAA+B;MACpE,WAAW;OAAE,MAAM;OAAU,aAAa;MAA2B;MACrE,WAAW;OAAE,MAAM;OAAU,aAAa;MAAyB;MACnE,SAAS;OAAE,MAAM;OAAW,aAAa;MAA8B;MACvE,gBAAgB;OAAE,MAAM;OAAW,aAAa;MAA4B;MAC5E,UAAU;OAAE,MAAM;OAAU,aAAa;MAAiC;KAC5E;KACA,UAAU,CAAC,SAAS;IACtB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY;MACV,WAAW;OAAE,MAAM;OAAU,aAAa;MAA2B;MACrE,OAAO;OAAE,MAAM;OAAU,aAAa;MAAmB;MACzD,WAAW;OAAE,MAAM;OAAU,aAAa;MAAuB;KACnE;KACA,UAAU,CAAC,WAAW;IACxB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY;MACV,SAAS;OAAE,MAAM;OAAU,aAAa;MAA2B;MACnE,SAAS;OAAE,MAAM;OAAW,aAAa;MAAoB;KAC/D;KACA,UAAU,CAAC,SAAS;IACtB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY;MACV,WAAW;OAAE,MAAM;OAAU,aAAa;MAA6B;MACvE,SAAS;OAAE,MAAM;OAAW,aAAa;MAAoB;MAC7D,OAAO;OAAE,MAAM;OAAW,aAAa;MAA2C;KACpF;KACA,UAAU,CAAC,WAAW;IACxB;GACF;GACA;IACE,MAAM;IACN,aAAa;IACb,aAAa;KACX,MAAM;KACN,YAAY,CAAC;IACf;GACF;EACF,EACF;CACF,CAAC;CAGD,OAAO,kBAAkB,uBAAuB,OAAO,YAA6B;EAClF,MAAM,WAAW,QAAQ,OAAO;EAChC,MAAM,OAAO,QAAQ,OAAO,aAAa,CAAC;EAE1C,IAAI;GACF,QAAQ,UAAR;IACE,KAAK,kBAEH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MADhB,QAAQ,cAAc;KACK,CAAC;KAAG,SAAS;IAAM;IAGzE,KAAK,gBAEH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MADd,QAAQ,YAAY,KAAK,KAAe;KACb,CAAC;KAAG,SAAS;IAAM;IAG3E,KAAK,iBAEH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MADZ,QAAQ,aAAa,KAAK,WAAqB;KACpB,CAAC;KAAG,SAAS;IAAM;IAG7E,KAAK,aAEH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MADhB,QAAQ,SAAS,KAAK,OAAiB;KACZ,CAAC;KAAG,SAAS;IAAM;IAGzE,KAAK,kBAEH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MADf,QAAQ,cAAc,KAAK,QAAoB;KACpB,CAAC;KAAG,SAAS;IAAM;IAG1E,KAAK,eAWH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAVV,QAAQ,WACrC,IAQF;KAC0D,CAAC;KAAG,SAAS;IAAM;IAG/E,KAAK,iBAOH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MANR,QAAQ,aACvC,IAIF;KAC4D,CAAC;KAAG,SAAS;IAAM;IAGjF,KAAK,aAaH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAZZ,QAAQ,SACnC,IAUF;KACwD,CAAC;KAAG,SAAS;IAAM;IAG7E,KAAK,eAQH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAPV,QAAQ,WACrC,IAKF;KAC0D,CAAC;KAAG,SAAS;IAAM;IAG/E,KAAK,eAOH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MANV,QAAQ,WACrC,IAIF;KAC0D,CAAC;KAAG,SAAS;IAAM;IAG/E,KAAK,iBAQH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAPR,QAAQ,aACvC,IAKF;KAC4D,CAAC;KAAG,SAAS;IAAM;IAGjF,KAAK,QAEH,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MADhB,QAAQ,KAAK;KACc,CAAC;KAAG,SAAS;IAAM;IAGzE,SACE,MAAM,IAAI,MAAM,iBAAiB,UAAU;GAC/C;EACF,SAAS,OAAO;GAGd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAE1E,OAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAQ,MAFf,iBAAiB,YAAY,eAAe,UAAU;IAElC,CAAC;IAChC,SAAS;GACX;EACF;CACF,CAAC;CAGD,MAAM,aAAa,cAAc,OAAO,KAAK,GAAG;CAChD,MAAM,YAAY,KAAK,QAAQ,UAAU;CACzC,MAAM,UAAU,KAAK,KAAK,WAAW,MAAM,MAAM;CAEjD,IAAI,CAAC,GAAG,WAAW,OAAO,GACxB,GAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;CAI3C,MAAM,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,QAAQ,SAAS,GAAG;CAC/D,MAAM,UAAU,KAAK,KAAK,SAAS,cAAc,UAAU,KAAK;CAGhE,MAAM,yBAAyB,qBAAqB;EAClD;EAEA,cAAc;GACZ,MAAM;GACN,KAAK,iBAAiB;EACxB;EAEA,MAAM,YAAY,SAAiC;GACjD,MAAM,WAAW;IACf,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;IAClC,WAAW;IACX;GACF;GAEA,GAAG,eAAe,SAAS,GAAG,KAAK,UAAU,QAAQ,EAAE,GAAG;GAK1D,MAHe,OAAO,eAAe,OAAO,eAAe,IAAI,CAGpD,CAAC,CAAC,YAAY,KAAK,MAAM,OAAO;EAC7C;EAEA,MAAM,cAAc,SAAiC;GACnD,KAAK;GACL,MAAM,WAAW;IACf,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;IAClC,WAAW;IACX,eAAe,KAAK;IACpB;GACF;GAEA,GAAG,eAAe,SAAS,GAAG,KAAK,UAAU,QAAQ,EAAE,GAAG;GAK1D,MAHe,OAAO,eAAe,OAAO,eAAe,IAAI,CAGpD,CAAC,CAAC,cAAc,KAAK,MAAM,OAAO;EAC/C;CACF;CAEA,MAAM,iBAAiB,IAAI,iBAAiB;CAE5C,IAAI;EACF,MAAM,OAAO,QAAQ,cAAc;EACnC,QAAQ,OAAO,MAAM,oDAAoD;CAC3E,SAAS,OAAgB;EACvB,QAAQ,OAAO,MAAM,+BAA+B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAAG;EAC9G,QAAQ,KAAK,CAAC;CAChB;AACF"}