remnote-mcp-server 0.14.1 → 0.15.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +82 -28
  3. package/dist/cli.d.ts +2 -1
  4. package/dist/cli.js +9 -2
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +14 -2
  7. package/dist/config.js +210 -10
  8. package/dist/config.js.map +1 -1
  9. package/dist/daemon.d.ts +61 -0
  10. package/dist/daemon.js +735 -0
  11. package/dist/daemon.js.map +1 -0
  12. package/dist/index.js +6 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/remnote-cli/cli.js +4 -0
  15. package/dist/remnote-cli/cli.js.map +1 -1
  16. package/dist/remnote-cli/client/mcp-server-client.js +7 -1
  17. package/dist/remnote-cli/client/mcp-server-client.js.map +1 -1
  18. package/dist/remnote-cli/commands/content-input.d.ts +0 -12
  19. package/dist/remnote-cli/commands/content-input.js +0 -20
  20. package/dist/remnote-cli/commands/content-input.js.map +1 -1
  21. package/dist/remnote-cli/commands/create.js +3 -3
  22. package/dist/remnote-cli/commands/create.js.map +1 -1
  23. package/dist/remnote-cli/commands/journal.js +3 -0
  24. package/dist/remnote-cli/commands/journal.js.map +1 -1
  25. package/dist/remnote-cli/commands/read.js +20 -2
  26. package/dist/remnote-cli/commands/read.js.map +1 -1
  27. package/dist/remnote-cli/commands/search.js +24 -5
  28. package/dist/remnote-cli/commands/search.js.map +1 -1
  29. package/dist/remnote-cli/commands/update.js +5 -24
  30. package/dist/remnote-cli/commands/update.js.map +1 -1
  31. package/dist/remnote-cli/commands/write-actions.d.ts +4 -0
  32. package/dist/remnote-cli/commands/write-actions.js +123 -0
  33. package/dist/remnote-cli/commands/write-actions.js.map +1 -0
  34. package/dist/schemas/remnote-schemas.d.ts +33 -10
  35. package/dist/schemas/remnote-schemas.js +58 -16
  36. package/dist/schemas/remnote-schemas.js.map +1 -1
  37. package/dist/tools/index.d.ts +1264 -12
  38. package/dist/tools/index.js +157 -47
  39. package/dist/tools/index.js.map +1 -1
  40. package/dist/websocket-server.d.ts +5 -0
  41. package/dist/websocket-server.js +64 -10
  42. package/dist/websocket-server.js.map +1 -1
  43. package/mcpb/remnote-local/README.md +4 -3
  44. package/mcpb/remnote-local/manifest.json +30 -28
  45. package/mcpb/remnote-local/package.json +1 -1
  46. package/mcpb/remnote-local/remnote-local.mcpb +0 -0
  47. package/mcpb/remnote-local/server/fallback-tools.generated.js +323 -0
  48. package/mcpb/remnote-local/server/index.js +7 -122
  49. package/package.json +5 -3
package/CHANGELOG.md CHANGED
@@ -7,6 +7,62 @@ Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.15.0] - 2026-05-15
11
+
12
+ ### Added
13
+
14
+ - Add split RemNote write tools: `remnote_insert_children`, `remnote_replace_children`, and `remnote_update_tags`, plus
15
+ matching `remnote-cli` commands for ordered child insertion, destructive child replacement, and exact-ID tag mutation.
16
+ - Add exact-ID tag assignment to `remnote_append_journal` and `remnote-cli journal --tag-ids`.
17
+ - Add `remnote-mcp-server daemon` lifecycle commands for detached background startup, status, logs, graceful shutdown,
18
+ duplicate-start protection, stable log routing, and macOS `launchd` login persistence.
19
+ - Add `~/.remnote-mcp-server/config.toml` for persistent server and daemon defaults, including ports, host, log levels,
20
+ file logs, and WebSocket request/response JSON Lines logs.
21
+ - Add end-user validation guidance for MCP setup checks, including a zero-config agent validation prompt.
22
+
23
+ ### Changed
24
+
25
+ - Change `tags` output metadata to preserve exact tag Rem IDs plus names as `{ tagRemId, name }` objects.
26
+ - Change `remnote_search_by_tag` and `remnote-cli search-by-tag --tag-id` to search by exact tag Rem ID instead of tag
27
+ name or alias lookup.
28
+ - Update `remnote_get_playbook` guidance for the split write tools and exact-ID tag fields without expanding the
29
+ playbook shape.
30
+ - Limit `remnote_update_note` and `remnote-cli update` to metadata title updates so child and tag writes use focused
31
+ non-duplicative interfaces.
32
+ - Change `remnote_create_note` and `remnote-cli create` to use exact tag Rem IDs via `tagRemIds` / `--tag-ids` instead
33
+ of name-based `tags` / `--tags`.
34
+ - Generate MCPB manifest tools and fallback tool metadata from the canonical server tool definitions instead of
35
+ maintaining those copies by hand.
36
+ - Add daemon startup, status, log, and macOS persistence pointers to installation, configuration, and troubleshooting
37
+ docs.
38
+ - Make daemon lifecycle commands launchd-aware on macOS when the LaunchAgent is installed, and document the shared
39
+ control surface.
40
+ - Update agent-assisted live integration safeguards so agents preflight port `3001`, refuse to run when an existing
41
+ server is listening, and use `run-agent-integration-test.sh --preflight-only` for the same guard as real live runs.
42
+ - Clarify setup documentation for the official bridge plugin, compatible `remnote-mcp-server` versions, and explicit
43
+ `remnote-mcp-server` versus `remnote-cli` documentation targets.
44
+
45
+ ### Fixed
46
+
47
+ - Fix OpenAI/Codex tool registration compatibility by removing top-level JSON Schema composition keywords from
48
+ advertised MCP tool input schemas while keeping server-side validation strict.
49
+ - Align MCPB manifest and fallback tool metadata with the split write tools so local Claude Desktop/MCPB clients no
50
+ longer see stale `remnote_update_note` content/tag fields or name-based create tags.
51
+ - Tighten runtime validation for split child insertion and tag updates while keeping advertised MCP JSON schemas
52
+ client-compatible.
53
+
54
+ ## [0.14.2] - 2026-05-08
55
+
56
+ ### Changed
57
+
58
+ - Reject RemNote bridge WebSocket connections that do not send a compatible bridge `hello.version`, with a clearer
59
+ disconnect reason and server log message pointing users to `MCP/OpenClaw Automation Bridge`.
60
+ - Added prominent README troubleshooting guidance for wrong or incompatible RemNote Marketplace plugin installs,
61
+ including the related `quentintou/remnote-mcp-bridge#8` report.
62
+ - Updated Claude Desktop / Cowork setup docs to clarify that local MCPB works for Cowork in the Claude Desktop app
63
+ when desktop extensions are enabled, while remote connectors remain required for web/mobile, cloud-hosted clients,
64
+ and managed deployments without local MCPB.
65
+
10
66
  ## [0.14.1] - 2026-05-08
11
67
 
12
68
  ### Added
package/README.md CHANGED
@@ -9,7 +9,19 @@ MCP server and CLI package that bridges AI agents, local scripts, and coding har
9
9
  [RemNote](https://remnote.com/) via the [RemNote Automation Bridge
10
10
  plugin](https://github.com/robert7/remnote-mcp-bridge).
11
11
 
12
- > If you run into any issues, please [report them here](https://github.com/robert7/remnote-mcp-server/issues).
12
+ > **Connection issue? Check the RemNote bridge plugin and server versions first.** Use the official
13
+ > **MCP/OpenClaw Automation Bridge** by Robert Spiegel in RemNote, and run a compatible `remnote-mcp-server` on the same
14
+ > `0.x` minor line (for example, `0.15.x` with `0.15.x`). Wrong plugin flavors or mismatched versions can disconnect
15
+ > with a `1008` compatibility message. If Marketplace and npm releases are temporarily out of sync, pin the matching
16
+ > server package or run matching bridge/server checkouts from source. Start with the
17
+ > [version compatibility guide](https://github.com/robert7/remnote-mcp-bridge/blob/main/docs/guides/bridge-consumer-version-compatibility.md),
18
+ > [plugin install guide](https://github.com/robert7/remnote-mcp-bridge/blob/main/docs/guides/install-plugin-via-marketplace-beginner.md),
19
+ > [local plugin guide](https://github.com/robert7/remnote-mcp-bridge/blob/main/docs/guides/development-run-plugin-locally.md), and
20
+ > [server installation guide](docs/guides/installation.md).
21
+ > After setup, see the [agent validation prompts](docs/agent-validation-prompts/README.md) to verify that your chosen AI
22
+ > agent can use the installed RemNote MCP tools end to end.
23
+ > If the guides do not resolve your problem, [open an issue](https://github.com/robert7/remnote-mcp-server/issues) with
24
+ > the relevant versions, setup path, observed behavior, and exact error/status message.
13
25
 
14
26
  ## What is This?
15
27
 
@@ -58,17 +70,19 @@ The server acts as a bridge:
58
70
  **Multi-Agent Support:** Multiple AI agents can connect simultaneously to the same RemNote knowledge base. Each agent
59
71
  gets its own MCP session while sharing the WebSocket bridge.
60
72
 
61
- **Remote Access:** By default, the server binds to localhost (127.0.0.1) for local AI agents. Cloud-based services like
62
- Claude Desktop and Claude Cowork require remote access—use tunneling tools like ngrok to expose the HTTP endpoint securely. The WebSocket
63
- connection always stays local for security. See [Remote Access Guide](docs/guides/remote-access.md) for setup.
73
+ **Remote Access:** By default, the server binds to localhost (127.0.0.1) for local AI agents. Claude Desktop and Claude
74
+ Cowork can use the bundled local MCPB extension when desktop extensions are enabled. Cloud-based clients, web/mobile
75
+ surfaces, and managed Claude deployments without local MCPB require remote access—use tunneling tools like ngrok to
76
+ expose the HTTP endpoint securely. The WebSocket connection always stays local for security. See
77
+ [Remote Access Guide](docs/guides/remote-access.md) for setup.
64
78
 
65
79
  ## Features
66
80
 
67
81
  - **Create Notes & Flashcards** - Create simple notes, hierarchical markdown trees, or RemNote-native flashcards
68
- - **Search Knowledge Base** - Run full-text searches or tag-based searches with ancestor context
82
+ - **Search Knowledge Base** - Run full-text searches or exact tag Rem ID searches with ancestor context
69
83
  - **Read Notes** - Retrieve note content in markdown or structured form with configurable traversal depth
70
- - **Update Notes** - Modify titles, append or replace hierarchical content, and manage tags
71
- - **Journal Entries** - Append timestamped daily entries, including hierarchical markdown content
84
+ - **Update Notes** - Modify titles, insert or replace hierarchical content, and manage tags by exact Rem ID
85
+ - **Journal Entries** - Append timestamped daily entries with hierarchical markdown content and optional exact tag Rem IDs
72
86
  - **Agent Playbook** - Return built-in navigation and safety guidance for MCP clients
73
87
  - **Connection Status** - Check server and plugin connection health
74
88
 
@@ -92,9 +106,10 @@ remnote-mcp-stdio --version
92
106
 
93
107
  ### 2. Install the RemNote Plugin
94
108
 
95
- Install the [RemNote Automation Bridge plugin](https://github.com/robert7/remnote-mcp-bridge) in your RemNote app.
96
- Currently available from GitHub; registration in the RemNote marketplace is pending approval. Configure the plugin
97
- to connect to `ws://127.0.0.1:3002`.
109
+ Install the official [MCP/OpenClaw Automation Bridge plugin](https://github.com/robert7/remnote-mcp-bridge) in your
110
+ RemNote app. If installing from the RemNote Marketplace, verify the plugin name and author; similarly named
111
+ `MCP Bridge` variants may be incompatible with this server and cause connection loops or `1008` disconnects. Configure
112
+ the plugin to connect to `ws://127.0.0.1:3002`.
98
113
 
99
114
  ### 3. Start the Server
100
115
 
@@ -105,17 +120,35 @@ remnote-mcp-server
105
120
  Expected output:
106
121
 
107
122
  ```text
108
- RemNote MCP Server v0.14.1 listening { wsPort: 3002, httpPort: 3001 }
123
+ RemNote MCP Server v<version> listening { wsPort: 3002, httpPort: 3001 }
109
124
  ```
110
125
 
111
126
  Keep this terminal running.
112
127
 
128
+ For a background server that survives terminal close and writes to a stable log file:
129
+
130
+ ```bash
131
+ remnote-mcp-server daemon start
132
+ remnote-mcp-server daemon status
133
+ remnote-mcp-server daemon logs
134
+ remnote-mcp-server daemon stop
135
+ ```
136
+
137
+ Daemon state and logs default to `~/.remnote-mcp-server/`. On macOS, install a login LaunchAgent for restart/login
138
+ persistence:
139
+
140
+ ```bash
141
+ remnote-mcp-server daemon install-launchd
142
+ ```
143
+
144
+ After installing the LaunchAgent, `remnote-mcp-server daemon status|start|stop|restart` controls the launchd service.
145
+
113
146
  ### 4. Configure Your AI Client
114
147
 
115
148
  - [Configuration Guide](docs/guides/configuration.md) - Overview and generic setup
116
149
  - [Codex TUI / Codex.app](docs/guides/configuration-codex.md) - HTTP MCP, stdio proxy, and `remnote-cli` skill setup
117
- - [Claude Desktop Local MCPB](docs/guides/configuration-claude-desktop-local-mcpb.md) - Preferred local Claude Desktop setup, no public HTTPS required
118
- - [Claude Desktop / Cowork](docs/guides/configuration-claude-desktop-cowork.md) - Remote connector setup when local MCPB is not applicable
150
+ - [Claude Desktop / Cowork Local MCPB](docs/guides/configuration-claude-desktop-local-mcpb.md) - Preferred local desktop setup, no public HTTPS required
151
+ - [Claude Desktop / Cowork Remote Connector](docs/guides/configuration-claude-desktop-cowork.md) - Remote connector setup when local MCPB is not applicable
119
152
  - [Claude Code CLI](docs/guides/configuration-claude-code-CLI.md) - Claude Code local MCP setup
120
153
  - [ChatGPT](docs/guides/configuration-chatgpt.md) - ChatGPT Apps configuration
121
154
  - [Accomplish](docs/guides/configuration-accomplish.md) - Accomplish (Openwork) configuration
@@ -137,10 +170,11 @@ Keep this terminal running.
137
170
 
138
171
  ### Usage
139
172
 
140
- - **[CLI Options Reference](docs/guides/cli-options.md)** - Command-line options and environment variables
141
- - **[remnote-cli Command Reference](docs/guides/cli-command-reference.md)** - Shell command reference for the bundled CLI
173
+ - **[remnote-mcp-server Command Reference](docs/guides/remnote-mcp-server-command-reference.md)** - Server executable, daemon, and launchd options
174
+ - **[remnote-cli Command Reference](docs/guides/remnote-cli-command-reference.md)** - Shell command reference for the bundled CLI
142
175
  - **[MCP Tools Reference](docs/guides/tools-reference.md)** - Detailed reference for all 9 RemNote tools
143
- - **[Remote Access Setup](docs/guides/remote-access.md)** - Expose server for Claude Desktop / Cowork (ngrok, etc.)
176
+ - **[Remote Access Setup](docs/guides/remote-access.md)** - Expose server for cloud clients or remote connector flows
177
+ (ngrok, etc.)
144
178
 
145
179
  ### Help & Advanced
146
180
 
@@ -150,21 +184,26 @@ Keep this terminal running.
150
184
  ### Development
151
185
 
152
186
  - **[Development Setup](docs/guides/development-setup.md)** - Contributing guide for developers
187
+ - **[Testing Strategy](docs/guides/testing-strategy.md)** - Three-level testing model: local quality checks, maintainer
188
+ live integration, and end-user agent validation
153
189
  - **[Integration Testing](docs/guides/integration-testing.md)** - Canonical shared workflow for updating and running MCP server + CLI integration coverage against live RemNote
154
190
  - **[Publishing Guide](docs/npm-publishing.md)** - npm publishing process (maintainers only)
155
191
 
156
192
  ## Available MCP Tools
157
193
 
158
- | Tool | Description |
159
- |---------------------------|------------------------------------------------|
160
- | `remnote_create_note` | Create notes, markdown trees, or flashcards with title, content, parent, and tags |
161
- | `remnote_search` | Search knowledge base with full-text search and parent-context metadata; `tags` remain optional and SDK-limited |
162
- | `remnote_search_by_tag` | Search by tag with ancestor-context resolution |
163
- | `remnote_read_note` | Read note by ID with metadata and markdown or structured content; readable `tags` remain SDK-limited |
164
- | `remnote_update_note` | Update title, append/replace content, or modify tags |
165
- | `remnote_append_journal` | Append hierarchical content to today's daily document |
194
+ | Tool | Description |
195
+ |---------------------------|-----------------------------------------------------------------------------|
196
+ | `remnote_create_note` | Create notes, markdown trees, or flashcards with optional exact tag Rem IDs |
197
+ | `remnote_search` | Search knowledge base with full-text search, parent context, and optional tag IDs/names |
198
+ | `remnote_search_by_tag` | Search by exact tag Rem ID with ancestor-context resolution |
199
+ | `remnote_read_note` | Read note by ID with metadata, optional tag IDs/names, and markdown or structured content |
200
+ | `remnote_update_note` | Update title |
201
+ | `remnote_insert_children` | Insert child Rems at deterministic positions |
202
+ | `remnote_replace_children` | Replace direct children when destructive replacement is enabled |
203
+ | `remnote_update_tags` | Add or remove tags by exact tag Rem ID |
204
+ | `remnote_append_journal` | Append hierarchical content to today's daily document with optional tag Rem IDs |
166
205
  | `remnote_read_table` | Read Advanced Table columns, rows, and typed property metadata |
167
- | `remnote_get_playbook` | Get recommended MCP usage/navigation playbook |
206
+ | `remnote_get_playbook` | Get recommended MCP usage/navigation playbook |
168
207
  | `remnote_status` | Check connection status and statistics |
169
208
 
170
209
  Tools that declare an `outputSchema` return MCP `structuredContent` plus a JSON `content` text block for compatibility.
@@ -181,7 +220,8 @@ See the [Tools Reference](docs/guides/tools-reference.md) for detailed usage and
181
220
 
182
221
  - **[Claude Code CLI](https://claude.com/claude-code)** - Local terminal-based agent
183
222
  - **Codex TUI / Codex.app** - Local OpenAI coding agent clients
184
- - **Claude Desktop / Cowork** - Remote connector clients (require [remote access](docs/guides/remote-access.md))
223
+ - **Claude Desktop / Cowork** - Local MCPB clients when desktop extensions are enabled, or remote connector clients
224
+ when local MCPB is not applicable
185
225
  - **[Accomplish](https://github.com/accomplish-ai/accomplish)** - Task-based MCP client (formerly Openwork)
186
226
  - **Any MCP client** supporting Streamable HTTP transport
187
227
  - **Any local MCP client** supporting stdio transport through `remnote-mcp-stdio`
@@ -233,7 +273,21 @@ remnote-mcp-server --http-port 3003 --ws-port 3004
233
273
 
234
274
  After changing ports, update your MCP client configuration and RemNote plugin settings.
235
275
 
236
- See [CLI Options Reference](docs/guides/cli-options.md) for all options.
276
+ ### Background Daemon
277
+
278
+ ```bash
279
+ remnote-mcp-server daemon start
280
+ ```
281
+
282
+ - Default log: `~/.remnote-mcp-server/remnote-mcp-server.log`
283
+ - Duplicate starts are treated as already running when the daemon PID is alive.
284
+ - If the configured HTTP or WebSocket port is already occupied, startup fails before spawning a second server.
285
+ - Use `remnote-mcp-server daemon stop` for graceful shutdown.
286
+ - Use `remnote-mcp-server daemon install-launchd` on macOS to keep the server running across login and unexpected
287
+ exits.
288
+ - When launchd is installed, the same `daemon status/start/stop/restart` commands control the launchd service.
289
+
290
+ See [remnote-mcp-server Command Reference](docs/guides/remnote-mcp-server-command-reference.md) for all options.
237
291
 
238
292
  ## Troubleshooting
239
293
 
@@ -283,7 +337,7 @@ See the [Development Setup Guide](docs/guides/development-setup.md) for complete
283
337
 
284
338
  Pull requests that affect bridge-consumer behavior should follow the shared PR rules in the bridge repo: [Pull Request Guide](https://github.com/robert7/remnote-mcp-bridge/blob/main/docs/guides/pull-request-guide.md). In particular, keep bridge and server-package behavior aligned for shared functionality changes.
285
339
 
286
- For the canonical workflow for updating and running shared live integration coverage, see the [Integration Testing Guide](docs/guides/integration-testing.md).
340
+ For the three-level testing model and links to each verification path, see the [Testing Strategy](docs/guides/testing-strategy.md).
287
341
 
288
342
  ## Related Projects
289
343
 
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export interface CliOptions {
2
+ config?: string;
2
3
  wsPort?: number;
3
4
  httpPort?: number;
4
5
  httpHost?: string;
@@ -13,7 +14,7 @@ export declare function getBundledMcpbPath(): string;
13
14
  /**
14
15
  * Handle utility commands that do not start the MCP server.
15
16
  */
16
- export declare function handleUtilityCommand(argv?: string[]): boolean;
17
+ export declare function handleUtilityCommand(argv?: string[]): Promise<boolean>;
17
18
  /**
18
19
  * Parse CLI arguments and return typed options
19
20
  */
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import { createRequire } from 'module';
3
3
  import { fileURLToPath } from 'url';
4
+ import { handleDaemonCommand, isDaemonCommand } from './daemon.js';
4
5
  const require = createRequire(import.meta.url);
5
6
  const packageJson = require('../package.json');
6
7
  const validLogLevels = ['debug', 'info', 'warn', 'error'];
@@ -11,8 +12,13 @@ export function getBundledMcpbPath() {
11
12
  /**
12
13
  * Handle utility commands that do not start the MCP server.
13
14
  */
14
- export function handleUtilityCommand(argv = process.argv) {
15
+ export async function handleUtilityCommand(argv = process.argv) {
15
16
  if (argv[2] !== MCPB_PATH_COMMAND) {
17
+ if (isDaemonCommand(argv)) {
18
+ const result = await handleDaemonCommand(argv);
19
+ process.exitCode = result.exitCode;
20
+ return result.handled;
21
+ }
16
22
  return false;
17
23
  }
18
24
  console.log(getBundledMcpbPath());
@@ -27,10 +33,11 @@ export function parseCliArgs() {
27
33
  .name('remnote-mcp-server')
28
34
  .description('MCP server bridge for RemNote knowledge base')
29
35
  .version(packageJson.version)
30
- .addHelpText('after', '\nCommands:\n mcpb-path Print the bundled Claude Desktop MCPB extension path')
36
+ .addHelpText('after', '\nCommands:\n mcpb-path Print the bundled Claude Desktop MCPB extension path\n daemon <action> Manage background server lifecycle and macOS launchd persistence')
31
37
  .option('--ws-port <number>', 'WebSocket port (default: 3002, env: REMNOTE_WS_PORT)', parsePort)
32
38
  .option('--http-port <number>', 'HTTP MCP port (default: 3001, env: REMNOTE_HTTP_PORT)', parsePort)
33
39
  .option('--http-host <host>', 'HTTP server bind address (default: 127.0.0.1, env: REMNOTE_HTTP_HOST). Use 0.0.0.0 for Docker/VPS deployments', validateHost)
40
+ .option('--config <path>', 'TOML config file path (default: ~/.remnote-mcp-server/config.toml)')
34
41
  .option('--log-level <level>', `Console log level: ${validLogLevels.join(', ')} (default: info)`, validateLogLevel)
35
42
  .option('--log-level-file <level>', `File log level (default: same as --log-level)`, validateLogLevel)
36
43
  .option('--verbose', 'Shorthand for --log-level debug')
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -11,8 +11,20 @@ export interface ServerConfig {
11
11
  responseLog?: string;
12
12
  prettyLogs: boolean;
13
13
  }
14
+ export interface ConfigFileOptions {
15
+ server?: Partial<Omit<CliOptions, 'config'>>;
16
+ daemon?: {
17
+ logFile?: string;
18
+ };
19
+ }
20
+ export interface ConfigLoadOptions {
21
+ homeDir?: string;
22
+ }
23
+ export declare const CONFIG_FILE_NAME = "config.toml";
14
24
  /**
15
25
  * Merge CLI options with environment variables and apply defaults
16
- * Precedence: CLI > Environment Variables > Defaults
26
+ * Precedence: CLI > Environment Variables > Config File > Defaults
17
27
  */
18
- export declare function getConfig(cliOptions: CliOptions): ServerConfig;
28
+ export declare function getConfig(cliOptions: CliOptions, loadOptions?: ConfigLoadOptions): ServerConfig;
29
+ export declare function loadConfigFileOptions(configPath?: string, loadOptions?: ConfigLoadOptions): ConfigFileOptions;
30
+ export declare function parseConfigToml(source: string, filePath?: string): ConfigFileOptions;
package/dist/config.js CHANGED
@@ -1,10 +1,17 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export const CONFIG_FILE_NAME = 'config.toml';
1
5
  /**
2
6
  * Merge CLI options with environment variables and apply defaults
3
- * Precedence: CLI > Environment Variables > Defaults
7
+ * Precedence: CLI > Environment Variables > Config File > Defaults
4
8
  */
5
- export function getConfig(cliOptions) {
9
+ export function getConfig(cliOptions, loadOptions = {}) {
10
+ const homeDir = loadOptions.homeDir ?? homedir();
11
+ const configFile = loadConfigFileOptions(cliOptions.config, loadOptions);
12
+ const configOptions = configFile.server ?? {};
6
13
  // Apply verbose flag override
7
- let logLevel = cliOptions.logLevel || 'info';
14
+ let logLevel = cliOptions.logLevel ?? (configOptions.verbose ? 'debug' : configOptions.logLevel) ?? 'info';
8
15
  if (cliOptions.verbose) {
9
16
  logLevel = 'debug';
10
17
  }
@@ -17,18 +24,30 @@ export function getConfig(cliOptions) {
17
24
  throw new Error(`Invalid HTTP port: ${cliOptions.httpPort}. Must be between 1 and 65535.`);
18
25
  }
19
26
  // Get ports with CLI > env > default precedence
20
- const wsPort = cliOptions.wsPort || parseInt(process.env.REMNOTE_WS_PORT || '3002', 10);
21
- const httpPort = cliOptions.httpPort || parseInt(process.env.REMNOTE_HTTP_PORT || '3001', 10);
27
+ const wsPort = cliOptions.wsPort ??
28
+ parseOptionalPortEnv(process.env.REMNOTE_WS_PORT) ??
29
+ configOptions.wsPort ??
30
+ 3002;
31
+ const httpPort = cliOptions.httpPort ??
32
+ parseOptionalPortEnv(process.env.REMNOTE_HTTP_PORT) ??
33
+ configOptions.httpPort ??
34
+ 3001;
22
35
  // Get hosts with CLI > env > default precedence
23
36
  // SECURITY: WebSocket ALWAYS binds to localhost, regardless of env var or CLI option
24
37
  const wsHost = '127.0.0.1';
25
- const httpHost = cliOptions.httpHost || process.env.REMNOTE_HTTP_HOST || '127.0.0.1';
38
+ const httpHost = cliOptions.httpHost ?? process.env.REMNOTE_HTTP_HOST ?? configOptions.httpHost ?? '127.0.0.1';
26
39
  // Validate port conflicts
27
40
  if (wsPort === httpPort) {
28
41
  throw new Error(`WebSocket port and HTTP port cannot be the same (both set to ${wsPort})`);
29
42
  }
43
+ const logFileOption = cliOptions.logFile ?? configOptions.logFile;
44
+ const logFile = logFileOption ? resolveTilde(logFileOption, homeDir) : undefined;
45
+ const requestLogOption = cliOptions.requestLog ?? configOptions.requestLog;
46
+ const requestLog = requestLogOption ? resolveTilde(requestLogOption, homeDir) : undefined;
47
+ const responseLogOption = cliOptions.responseLog ?? configOptions.responseLog;
48
+ const responseLog = responseLogOption ? resolveTilde(responseLogOption, homeDir) : undefined;
30
49
  // File log level defaults to console log level if not specified
31
- const logLevelFile = cliOptions.logLevelFile || (cliOptions.logFile ? logLevel : undefined);
50
+ const logLevelFile = cliOptions.logLevelFile ?? configOptions.logLevelFile ?? (logFile ? logLevel : undefined);
32
51
  // Pretty logs in development (when using pino-pretty)
33
52
  const prettyLogs = process.stdout.isTTY === true;
34
53
  return {
@@ -38,10 +57,191 @@ export function getConfig(cliOptions) {
38
57
  httpHost,
39
58
  logLevel,
40
59
  logLevelFile,
41
- logFile: cliOptions.logFile,
42
- requestLog: cliOptions.requestLog,
43
- responseLog: cliOptions.responseLog,
60
+ logFile,
61
+ requestLog,
62
+ responseLog,
44
63
  prettyLogs,
45
64
  };
46
65
  }
66
+ export function loadConfigFileOptions(configPath, loadOptions = {}) {
67
+ const homeDir = loadOptions.homeDir ?? homedir();
68
+ const resolvedPath = resolveTilde(configPath ?? join('~', '.remnote-mcp-server', CONFIG_FILE_NAME), homeDir);
69
+ const explicitPath = configPath !== undefined;
70
+ if (!existsSync(resolvedPath)) {
71
+ if (explicitPath) {
72
+ throw new Error(`Config file not found: ${resolvedPath}`);
73
+ }
74
+ return {};
75
+ }
76
+ return parseConfigToml(readFileSync(resolvedPath, 'utf8'), resolvedPath);
77
+ }
78
+ export function parseConfigToml(source, filePath = CONFIG_FILE_NAME) {
79
+ const config = {};
80
+ let section;
81
+ source.split(/\r?\n/).forEach((rawLine, index) => {
82
+ const lineNumber = index + 1;
83
+ const line = stripTomlComment(rawLine).trim();
84
+ if (!line) {
85
+ return;
86
+ }
87
+ const sectionMatch = line.match(/^\[([A-Za-z0-9_-]+)\]$/);
88
+ if (sectionMatch) {
89
+ const parsedSection = sectionMatch[1];
90
+ if (parsedSection !== 'server' && parsedSection !== 'daemon') {
91
+ throw new Error(`${filePath}:${lineNumber}: Unknown config section [${parsedSection}]`);
92
+ }
93
+ section = parsedSection;
94
+ config[section] ??= {};
95
+ return;
96
+ }
97
+ if (!section) {
98
+ throw new Error(`${filePath}:${lineNumber}: Config values must be under [server] or [daemon]`);
99
+ }
100
+ const keyValueMatch = line.match(/^([A-Za-z][A-Za-z0-9_]*)\s*=\s*(.+)$/);
101
+ if (!keyValueMatch) {
102
+ throw new Error(`${filePath}:${lineNumber}: Invalid TOML assignment`);
103
+ }
104
+ const [, key, rawValue] = keyValueMatch;
105
+ const value = parseTomlValue(rawValue.trim(), filePath, lineNumber);
106
+ assignConfigValue(config, section, key, value, filePath, lineNumber);
107
+ });
108
+ return config;
109
+ }
110
+ function assignConfigValue(config, section, key, value, filePath, lineNumber) {
111
+ if (section === 'daemon') {
112
+ if (key !== 'logFile') {
113
+ throw new Error(`${filePath}:${lineNumber}: Unknown daemon config key "${key}"`);
114
+ }
115
+ config.daemon ??= {};
116
+ config.daemon.logFile = expectString(value, filePath, lineNumber, key);
117
+ return;
118
+ }
119
+ config.server ??= {};
120
+ switch (key) {
121
+ case 'wsPort':
122
+ config.server.wsPort = expectPort(value, filePath, lineNumber, key);
123
+ return;
124
+ case 'httpPort':
125
+ config.server.httpPort = expectPort(value, filePath, lineNumber, key);
126
+ return;
127
+ case 'httpHost':
128
+ config.server.httpHost = validateHost(expectString(value, filePath, lineNumber, key), filePath, lineNumber);
129
+ return;
130
+ case 'logLevel':
131
+ config.server.logLevel = validateLogLevel(expectString(value, filePath, lineNumber, key), filePath, lineNumber);
132
+ return;
133
+ case 'logLevelFile':
134
+ config.server.logLevelFile = validateLogLevel(expectString(value, filePath, lineNumber, key), filePath, lineNumber);
135
+ return;
136
+ case 'verbose':
137
+ config.server.verbose = expectBoolean(value, filePath, lineNumber, key);
138
+ return;
139
+ case 'logFile':
140
+ config.server.logFile = expectString(value, filePath, lineNumber, key);
141
+ return;
142
+ case 'requestLog':
143
+ config.server.requestLog = expectString(value, filePath, lineNumber, key);
144
+ return;
145
+ case 'responseLog':
146
+ config.server.responseLog = expectString(value, filePath, lineNumber, key);
147
+ return;
148
+ default:
149
+ throw new Error(`${filePath}:${lineNumber}: Unknown server config key "${key}"`);
150
+ }
151
+ }
152
+ function parseOptionalPortEnv(value) {
153
+ if (value === undefined) {
154
+ return undefined;
155
+ }
156
+ return parseInt(value, 10);
157
+ }
158
+ function parseTomlValue(value, filePath, lineNumber) {
159
+ if (value === 'true') {
160
+ return true;
161
+ }
162
+ if (value === 'false') {
163
+ return false;
164
+ }
165
+ if (/^-?\d+$/.test(value)) {
166
+ return Number(value);
167
+ }
168
+ if (value.startsWith('"') && value.endsWith('"')) {
169
+ return value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
170
+ }
171
+ throw new Error(`${filePath}:${lineNumber}: Unsupported TOML value`);
172
+ }
173
+ function stripTomlComment(line) {
174
+ let inString = false;
175
+ let escaped = false;
176
+ for (let i = 0; i < line.length; i += 1) {
177
+ const char = line[i];
178
+ if (escaped) {
179
+ escaped = false;
180
+ continue;
181
+ }
182
+ if (char === '\\' && inString) {
183
+ escaped = true;
184
+ continue;
185
+ }
186
+ if (char === '"') {
187
+ inString = !inString;
188
+ continue;
189
+ }
190
+ if (char === '#' && !inString) {
191
+ return line.slice(0, i);
192
+ }
193
+ }
194
+ return line;
195
+ }
196
+ function expectString(value, filePath, lineNumber, key) {
197
+ if (typeof value !== 'string') {
198
+ throw new Error(`${filePath}:${lineNumber}: Config key "${key}" must be a string`);
199
+ }
200
+ return value;
201
+ }
202
+ function expectBoolean(value, filePath, lineNumber, key) {
203
+ if (typeof value !== 'boolean') {
204
+ throw new Error(`${filePath}:${lineNumber}: Config key "${key}" must be a boolean`);
205
+ }
206
+ return value;
207
+ }
208
+ function expectPort(value, filePath, lineNumber, key) {
209
+ if (typeof value !== 'number') {
210
+ throw new Error(`${filePath}:${lineNumber}: Config key "${key}" must be a number`);
211
+ }
212
+ if (value < 1 || value > 65535) {
213
+ throw new Error(`${filePath}:${lineNumber}: Config key "${key}" must be between 1 and 65535`);
214
+ }
215
+ return value;
216
+ }
217
+ function validateLogLevel(value, filePath, lineNumber) {
218
+ const normalized = value.toLowerCase();
219
+ if (!['debug', 'info', 'warn', 'error'].includes(normalized)) {
220
+ throw new Error(`${filePath}:${lineNumber}: Invalid log level "${value}"`);
221
+ }
222
+ return normalized;
223
+ }
224
+ function validateHost(value, filePath, lineNumber) {
225
+ if (value === 'localhost' || value === '127.0.0.1' || value === '0.0.0.0') {
226
+ return value;
227
+ }
228
+ const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
229
+ if (!ipv4Pattern.test(value)) {
230
+ throw new Error(`${filePath}:${lineNumber}: Invalid HTTP host "${value}"`);
231
+ }
232
+ const octets = value.split('.').map(Number);
233
+ if (octets.some((octet) => octet < 0 || octet > 255)) {
234
+ throw new Error(`${filePath}:${lineNumber}: Invalid HTTP host "${value}"`);
235
+ }
236
+ return value;
237
+ }
238
+ function resolveTilde(pathValue, homeDir) {
239
+ if (pathValue === '~') {
240
+ return homeDir;
241
+ }
242
+ if (pathValue.startsWith('~/')) {
243
+ return join(homeDir, pathValue.slice(2));
244
+ }
245
+ return pathValue;
246
+ }
47
247
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,UAAsB;IAC9C,8BAA8B;IAC9B,IAAI,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,MAAM,CAAC;IAC7C,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,QAAQ,GAAG,OAAO,CAAC;IACrB,CAAC;IAED,0CAA0C;IAC1C,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,2BAA2B,UAAU,CAAC,MAAM,gCAAgC,CAAC,CAAC;IAChG,CAAC;IACD,IACE,UAAU,CAAC,QAAQ,KAAK,SAAS;QACjC,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC,EACxD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,CAAC,QAAQ,gCAAgC,CAAC,CAAC;IAC7F,CAAC;IAED,gDAAgD;IAChD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IACxF,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAE9F,gDAAgD;IAChD,qFAAqF;IACrF,MAAM,MAAM,GAAG,WAAW,CAAC;IAC3B,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,WAAW,CAAC;IAErF,0BAA0B;IAC1B,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,gEAAgE,MAAM,GAAG,CAAC,CAAC;IAC7F,CAAC;IAED,gEAAgE;IAChE,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE5F,sDAAsD;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;IAEjD,OAAO;QACL,MAAM;QACN,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,YAAY;QACZ,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,UAAU;KACX,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA2BjC,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAE9C;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,UAAsB,EACtB,cAAiC,EAAE;IAEnC,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;IACjD,MAAM,UAAU,GAAG,qBAAqB,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACzE,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC;IAE9C,8BAA8B;IAC9B,IAAI,QAAQ,GACV,UAAU,CAAC,QAAQ,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC;IAC9F,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,QAAQ,GAAG,OAAO,CAAC;IACrB,CAAC;IAED,0CAA0C;IAC1C,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,2BAA2B,UAAU,CAAC,MAAM,gCAAgC,CAAC,CAAC;IAChG,CAAC;IACD,IACE,UAAU,CAAC,QAAQ,KAAK,SAAS;QACjC,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC,EACxD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,CAAC,QAAQ,gCAAgC,CAAC,CAAC;IAC7F,CAAC;IAED,gDAAgD;IAChD,MAAM,MAAM,GACV,UAAU,CAAC,MAAM;QACjB,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QACjD,aAAa,CAAC,MAAM;QACpB,IAAI,CAAC;IACP,MAAM,QAAQ,GACZ,UAAU,CAAC,QAAQ;QACnB,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACnD,aAAa,CAAC,QAAQ;QACtB,IAAI,CAAC;IAEP,gDAAgD;IAChD,qFAAqF;IACrF,MAAM,MAAM,GAAG,WAAW,CAAC;IAC3B,MAAM,QAAQ,GACZ,UAAU,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,aAAa,CAAC,QAAQ,IAAI,WAAW,CAAC;IAEhG,0BAA0B;IAC1B,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,gEAAgE,MAAM,GAAG,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC;IAClE,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,MAAM,gBAAgB,GAAG,UAAU,CAAC,UAAU,IAAI,aAAa,CAAC,UAAU,CAAC;IAC3E,MAAM,UAAU,GAAG,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,MAAM,iBAAiB,GAAG,UAAU,CAAC,WAAW,IAAI,aAAa,CAAC,WAAW,CAAC;IAC9E,MAAM,WAAW,GAAG,iBAAiB,CAAC,CAAC,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7F,gEAAgE;IAChE,MAAM,YAAY,GAChB,UAAU,CAAC,YAAY,IAAI,aAAa,CAAC,YAAY,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE5F,sDAAsD;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;IAEjD,OAAO;QACL,MAAM;QACN,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,YAAY;QACZ,OAAO;QACP,UAAU;QACV,WAAW;QACX,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,UAAmB,EACnB,cAAiC,EAAE;IAEnC,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;IACjD,MAAM,YAAY,GAAG,YAAY,CAC/B,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,EAChE,OAAO,CACR,CAAC;IACF,MAAM,YAAY,GAAG,UAAU,KAAK,SAAS,CAAC;IAE9C,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,eAAe,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,QAAQ,GAAG,gBAAgB;IACzE,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,IAAI,OAAwC,CAAC;IAE7C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,KAAK,GAAG,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC1D,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,aAAa,KAAK,QAAQ,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;gBAC7D,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,6BAA6B,aAAa,GAAG,CAAC,CAAC;YAC1F,CAAC;YACD,OAAO,GAAG,aAAa,CAAC;YACxB,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,IAAI,UAAU,oDAAoD,CAC9E,CAAC;QACJ,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACzE,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,2BAA2B,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,aAAa,CAAC;QACxC,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QACpE,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAyB,EACzB,OAA4B,EAC5B,GAAW,EACX,KAAgC,EAChC,QAAgB,EAChB,UAAkB;IAElB,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,gCAAgC,GAAG,GAAG,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,CAAC,MAAM,KAAK,EAAE,CAAC;QACrB,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IAED,MAAM,CAAC,MAAM,KAAK,EAAE,CAAC;IACrB,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,QAAQ;YACX,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YACpE,OAAO;QACT,KAAK,UAAU;YACb,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YACtE,OAAO;QACT,KAAK,UAAU;YACb,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,YAAY,CACnC,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,EAC9C,QAAQ,EACR,UAAU,CACX,CAAC;YACF,OAAO;QACT,KAAK,UAAU;YACb,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,gBAAgB,CACvC,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,EAC9C,QAAQ,EACR,UAAU,CACX,CAAC;YACF,OAAO;QACT,KAAK,cAAc;YACjB,MAAM,CAAC,MAAM,CAAC,YAAY,GAAG,gBAAgB,CAC3C,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,EAC9C,QAAQ,EACR,UAAU,CACX,CAAC;YACF,OAAO;QACT,KAAK,SAAS;YACZ,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YACxE,OAAO;QACT,KAAK,SAAS;YACZ,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YACvE,OAAO;QACT,KAAK,YAAY;YACf,MAAM,CAAC,MAAM,CAAC,UAAU,GAAG,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YAC1E,OAAO;QACT,KAAK,aAAa;YAChB,MAAM,CAAC,MAAM,CAAC,WAAW,GAAG,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;YAC3E,OAAO;QACT;YACE,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,gCAAgC,GAAG,GAAG,CAAC,CAAC;IACrF,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAyB;IACrD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,cAAc,CACrB,KAAa,EACb,QAAgB,EAChB,UAAkB;IAElB,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,0BAA0B,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC9B,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CACnB,KAAgC,EAChC,QAAgB,EAChB,UAAkB,EAClB,GAAW;IAEX,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,iBAAiB,GAAG,oBAAoB,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CACpB,KAAgC,EAChC,QAAgB,EAChB,UAAkB,EAClB,GAAW;IAEX,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,iBAAiB,GAAG,qBAAqB,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CACjB,KAAgC,EAChC,QAAgB,EAChB,UAAkB,EAClB,GAAW;IAEX,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,iBAAiB,GAAG,oBAAoB,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,iBAAiB,GAAG,+BAA+B,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAkB;IAC3E,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,wBAAwB,KAAK,GAAG,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAkB;IACvE,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,yBAAyB,CAAC;IAC9C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,wBAAwB,KAAK,GAAG,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,IAAI,UAAU,wBAAwB,KAAK,GAAG,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,SAAiB,EAAE,OAAe;IACtD,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}