@j0hanz/filesystem-mcp 2.2.0 → 2.4.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/README.md +38 -32
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +21 -40
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -46
- package/dist/cli.js.map +1 -1
- package/dist/core/concurrency.d.ts +5 -17
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +32 -40
- package/dist/core/concurrency.js.map +1 -1
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/cursor.d.ts +1 -12
- package/dist/core/cursor.d.ts.map +1 -1
- package/dist/core/cursor.js +2 -12
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/errors.d.ts +6 -11
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +62 -117
- package/dist/core/errors.js.map +1 -1
- package/dist/core/file-uri.d.ts +7 -3
- package/dist/core/file-uri.d.ts.map +1 -1
- package/dist/core/file-uri.js +8 -4
- package/dist/core/file-uri.js.map +1 -1
- package/dist/core/fmt.d.ts +0 -11
- package/dist/core/fmt.d.ts.map +1 -1
- package/dist/core/fmt.js +1 -19
- package/dist/core/fmt.js.map +1 -1
- package/dist/core/fs.d.ts +34 -4
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +151 -12
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +1 -2
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +12 -28
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +19 -29
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +58 -73
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/observability.d.ts +9 -0
- package/dist/core/observability.d.ts.map +1 -1
- package/dist/core/observability.js +13 -2
- package/dist/core/observability.js.map +1 -1
- package/dist/core/path-completer.d.ts +6 -9
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +28 -55
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/path-discovery.d.ts.map +1 -1
- package/dist/core/path-discovery.js +3 -4
- package/dist/core/path-discovery.js.map +1 -1
- package/dist/core/path-utils.d.ts +28 -0
- package/dist/core/path-utils.d.ts.map +1 -1
- package/dist/core/path-utils.js +57 -2
- package/dist/core/path-utils.js.map +1 -1
- package/dist/core/path.d.ts +3 -8
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +8 -9
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +3 -22
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +46 -96
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts +8 -9
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +11 -14
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +6 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +21 -23
- package/dist/core/search.js.map +1 -1
- package/dist/core/sensitive.d.ts +4 -2
- package/dist/core/sensitive.d.ts.map +1 -1
- package/dist/core/sensitive.js +262 -45
- package/dist/core/sensitive.js.map +1 -1
- package/dist/core/store.d.ts +41 -16
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +147 -82
- package/dist/core/store.js.map +1 -1
- package/dist/core/util.d.ts +3 -2
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +5 -6
- package/dist/core/util.js.map +1 -1
- package/dist/core/watcher-registry.d.ts +1 -1
- package/dist/core/watcher-registry.d.ts.map +1 -1
- package/dist/core/watcher-registry.js +9 -5
- package/dist/core/watcher-registry.js.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/instructions.js +2 -2
- package/dist/instructions.js.map +1 -1
- package/dist/prompts.js +1 -1
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts +11 -19
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +51 -62
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts +4 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +15 -11
- package/dist/server.js.map +1 -1
- package/dist/tools/batch.d.ts +2 -2
- package/dist/tools/batch.js +3 -3
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.d.ts +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +168 -17
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +15 -9
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +48 -94
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +23 -23
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/diff.d.ts +1 -1
- package/dist/tools/diff.d.ts.map +1 -1
- package/dist/tools/diff.js +7 -37
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +101 -92
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/find-files.d.ts +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +18 -37
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/index.d.ts +10 -10
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +14 -13
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-roots.d.ts +1 -1
- package/dist/tools/list-roots.js +3 -3
- package/dist/tools/list-roots.js.map +1 -1
- package/dist/tools/list.d.ts +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +23 -29
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +31 -45
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts +1 -1
- package/dist/tools/patch.js +7 -7
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/progress.d.ts +17 -28
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +42 -94
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +10 -12
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +32 -49
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/tools/search-text.d.ts +1 -1
- package/dist/tools/search-text.d.ts.map +1 -1
- package/dist/tools/search-text.js +82 -45
- package/dist/tools/search-text.js.map +1 -1
- package/dist/tools/stat.d.ts +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +9 -29
- package/dist/tools/stat.js.map +1 -1
- package/dist/transport/http-policy.js +4 -4
- package/dist/transport/http-policy.js.map +1 -1
- package/dist/transport/http.d.ts +2 -2
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +21 -18
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/shared.d.ts +2 -2
- package/dist/transport/shared.js +2 -2
- package/dist/transport/shared.js.map +1 -1
- package/dist/transport/stdio.d.ts +4 -3
- package/dist/transport/stdio.d.ts.map +1 -1
- package/dist/transport/stdio.js +7 -6
- package/dist/transport/stdio.js.map +1 -1
- package/dist/transport.d.ts +2 -2
- package/dist/transport.js +2 -2
- package/dist/transport.js.map +1 -1
- package/package.json +6 -13
- package/dist/core/page-store.d.ts +0 -41
- package/dist/core/page-store.d.ts.map +0 -1
- package/dist/core/page-store.js +0 -74
- package/dist/core/page-store.js.map +0 -1
- package/dist/core/primitives.d.ts +0 -34
- package/dist/core/primitives.d.ts.map +0 -1
- package/dist/core/primitives.js +0 -63
- package/dist/core/primitives.js.map +0 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Filesystem-MCP is a [Model Context Protocol](https://modelcontextprotocol.io) se
|
|
|
23
23
|
| **Path guarding** | Every path is validated against allowed roots; `.env`, `*.pem`, `*id_rsa*` and similar patterns are denied |
|
|
24
24
|
| **Filesystem tools** | Navigate, inspect, read, and write across all major file operations |
|
|
25
25
|
| **Batch operations** | Most tools accept `path`, `paths[]`, or `files[]` for parallel execution |
|
|
26
|
-
| **Dual transport** | stdio by default; `--port` enables Streamable HTTP
|
|
26
|
+
| **Dual transport** | stdio by default; `--port` enables Streamable HTTP for both 2025-era and 2026-07-28 clients |
|
|
27
27
|
| **File subscriptions** | Resource subscriptions push change notifications when watched files update |
|
|
28
28
|
| **Regex safety** | RE2 in all search tools: linear-time matching, so no pattern can ReDoS the server |
|
|
29
29
|
|
|
@@ -231,14 +231,14 @@ All tools are scoped to the configured roots. Call `list_roots` first to discove
|
|
|
231
231
|
|
|
232
232
|
#### Write
|
|
233
233
|
|
|
234
|
-
| Tool | Description
|
|
235
|
-
| :------------- |
|
|
236
|
-
| `create` | Create one or more files,
|
|
237
|
-
| `edit` | Apply sequential literal string replacements to one or more files (max 5 per call).
|
|
238
|
-
| `move` | Move, rename, or copy (`copy: true`) one or more files/directories to explicit destinations.
|
|
239
|
-
| `delete` | Permanently delete one or more files or directories. This action is irreversible.
|
|
240
|
-
| `replace_text` | Bulk search-and-replace across files matching a glob pattern.
|
|
241
|
-
| `patch` | Apply a single-file unified diff and write the result.
|
|
234
|
+
| Tool | Description |
|
|
235
|
+
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
236
|
+
| `create` | Create one or more files, creating parent directories as needed. An existing file prompts the user to confirm the overwrite; `overwrite: true` on an entry skips the prompt, `append: true` adds to the end instead. |
|
|
237
|
+
| `edit` | Apply sequential literal string replacements to one or more files (max 5 per call). |
|
|
238
|
+
| `move` | Move, rename, or copy (`copy: true`) one or more files/directories to explicit destinations. |
|
|
239
|
+
| `delete` | Permanently delete one or more files or directories. This action is irreversible. |
|
|
240
|
+
| `replace_text` | Bulk search-and-replace across files matching a glob pattern. |
|
|
241
|
+
| `patch` | Apply a single-file unified diff and write the result. |
|
|
242
242
|
|
|
243
243
|
### Resources
|
|
244
244
|
|
|
@@ -298,6 +298,12 @@ concrete path and approving the elicitation-backed grant. `list_roots` reports
|
|
|
298
298
|
the roots already configured or accepted; it cannot discover an unknown
|
|
299
299
|
workspace by itself.
|
|
300
300
|
|
|
301
|
+
Over HTTP, 2025-era clients are served statelessly: tools, resources and
|
|
302
|
+
prompts work. Confirmations (recursive delete, overwrite, access grants) need a
|
|
303
|
+
2026-07-28 client or stdio and answer with a tool error saying so; file
|
|
304
|
+
subscriptions are not advertised on that leg, and a `resources/subscribe` sent
|
|
305
|
+
anyway is refused with method-not-found.
|
|
306
|
+
|
|
301
307
|
### Recommended global recipes
|
|
302
308
|
|
|
303
309
|
#### VS Code / Cursor / Claude Code (primary recipe)
|
|
@@ -354,24 +360,28 @@ filesystem-mcp /path/to/project1 /path/to/project2
|
|
|
354
360
|
|
|
355
361
|
#### CLI flags
|
|
356
362
|
|
|
357
|
-
| Flag | Default | Purpose
|
|
358
|
-
| :------------------------ | :------ |
|
|
359
|
-
| `[dirs...]` | — | One or more allowed root directories (positional)
|
|
360
|
-
| `--allow-cwd` | `false` | Also allow the current working directory as a root
|
|
361
|
-
| `--walk-cwd` | `false` | Walk up from CWD to find a project root; implies `--allow-cwd`
|
|
362
|
-
| `--allow-missing-roots` | `false` | Start even if configured allowed directories do not exist
|
|
363
|
-
| `--port <n>` | — | Enable Streamable HTTP transport on the given port (env: `FS_PORT`)
|
|
364
|
-
| `--http-host <host>` | — | HTTP server bind address (env: `FS_HTTP_HOST`)
|
|
365
|
-
| `--api-key <key>` | — | Require this API key on HTTP requests (env: `FS_API_KEY`)
|
|
366
|
-
| `--read-only` | `false` | Disable write tools: `create`, `edit`, `delete`, `move`, `patch`, `replace_text`
|
|
367
|
-
| `--
|
|
368
|
-
| `--
|
|
369
|
-
| `--allow-sensitive` | `false` | Allow access to sensitive system paths (env: `FS_ALLOW_SENSITIVE`)
|
|
370
|
-
| `--root-boundary <path>` | — | Require all allowed roots to fall under this path (env: `FS_ROOT_BOUNDARY`)
|
|
371
|
-
| `--max-file-size <bytes>` | — | Maximum file size for reads in bytes (env: `FS_MAX_FILE_SIZE`)
|
|
372
|
-
| `--log-level <level>` | `info` | RFC 5424 log level, `debug` through `emergency` (env: `FS_LOG_LEVEL`)
|
|
373
|
-
| `--print-config` | `false` | Print the active configuration and exit
|
|
374
|
-
|
|
363
|
+
| Flag | Default | Purpose |
|
|
364
|
+
| :------------------------ | :------ | :------------------------------------------------------------------------------------------------------------------------------------ |
|
|
365
|
+
| `[dirs...]` | — | One or more allowed root directories (positional) |
|
|
366
|
+
| `--allow-cwd` | `false` | Also allow the current working directory as a root |
|
|
367
|
+
| `--walk-cwd` | `false` | Walk up from CWD to find a project root; implies `--allow-cwd` |
|
|
368
|
+
| `--allow-missing-roots` | `false` | Start even if configured allowed directories do not exist |
|
|
369
|
+
| `--port <n>` | — | Enable Streamable HTTP transport on the given port (env: `FS_PORT`) |
|
|
370
|
+
| `--http-host <host>` | — | HTTP server bind address (env: `FS_HTTP_HOST`) |
|
|
371
|
+
| `--api-key <key>` | — | Require this API key on HTTP requests (env: `FS_API_KEY`) |
|
|
372
|
+
| `--read-only` | `false` | Disable write tools: `create`, `edit`, `delete`, `move`, `patch`, `replace_text` |
|
|
373
|
+
| `--deny <pattern>` | — | Block paths matching this pattern; repeatable |
|
|
374
|
+
| `--allow <pattern>` | — | Exempt a pattern from the built-in sensitive denylist; repeatable (env: `FS_ALLOWLIST`). Does not lift `--deny`/`FS_DENYLIST` entries |
|
|
375
|
+
| `--allow-sensitive` | `false` | Allow access to sensitive system paths (env: `FS_ALLOW_SENSITIVE`) |
|
|
376
|
+
| `--root-boundary <path>` | — | Require all allowed roots to fall under this path (env: `FS_ROOT_BOUNDARY`) |
|
|
377
|
+
| `--max-file-size <bytes>` | — | Maximum file size for reads in bytes (env: `FS_MAX_FILE_SIZE`) |
|
|
378
|
+
| `--log-level <level>` | `info` | RFC 5424 log level, `debug` through `emergency` (env: `FS_LOG_LEVEL`) |
|
|
379
|
+
| `--print-config` | `false` | Print the active configuration as JSON and exit |
|
|
380
|
+
|
|
381
|
+
`--deny` and `--allow` patterns support `*` (any run within a segment),
|
|
382
|
+
`**` (any run of segments), `?`, `[...]` classes, and `{a,b}` alternation.
|
|
383
|
+
Dot-leading (hidden) names match like any other — `secrets/**` denies
|
|
384
|
+
`secrets/.env`, `*id_rsa*` denies `.id_rsa`.
|
|
375
385
|
|
|
376
386
|
#### Environment variables
|
|
377
387
|
|
|
@@ -387,6 +397,7 @@ Flags take precedence when both are set.
|
|
|
387
397
|
| `FS_ALLOW_MISSING_ROOTS` | Start even if configured directories do not exist (mirrors `--allow-missing-roots`). |
|
|
388
398
|
| `FS_ALLOW_SENSITIVE` | Allow access to sensitive system paths (mirrors `--allow-sensitive`). |
|
|
389
399
|
| `FS_DENYLIST` | Comma-separated list of paths or patterns to block (mirrors `--deny`). |
|
|
400
|
+
| `FS_ALLOWLIST` | Comma-separated patterns exempted from the built-in sensitive denylist (mirrors `--allow`). Never lifts `FS_DENYLIST`/`--deny` entries. |
|
|
390
401
|
| `FS_MAX_FILE_SIZE` | Maximum file size for reads in bytes (mirrors `--max-file-size`). |
|
|
391
402
|
| `FS_LOG_LEVEL` | RFC 5424 log level: `debug`, `info`, `notice`, `warn`/`warning`, `error`, `critical`, `alert`, or `emergency` (mirrors `--log-level`). |
|
|
392
403
|
| `FS_PORT` | Start the Streamable HTTP transport on this port; unset = stdio (mirrors `--port`). |
|
|
@@ -398,12 +409,7 @@ Flags take precedence when both are set.
|
|
|
398
409
|
| `FS_ALLOW_UNRESTRICTED_HOSTS` | Bind a wildcard host with no Host validation (accepts the risk). |
|
|
399
410
|
| `FS_PUBLIC_URL` | Resource identifier URL for RFC 9728 discovery. |
|
|
400
411
|
| `FS_RATE_LIMIT_RPM` | Per-client-IP requests/minute (default 120 with API-key authentication, 6,000 for keyless loopback; range 1–100000). |
|
|
401
|
-
| `FS_MAX_REQUEST_BYTES` | Max HTTP request body bytes (default 4194304, 1024–268435456). |
|
|
402
|
-
| `FS_KEEPALIVE_TIMEOUT_MS` | HTTP keep-alive timeout in ms; set above any fronting proxy's idle timeout (default 5000, 1000–600000). |
|
|
403
412
|
| `FS_MAX_WATCHERS` | Max concurrent file watchers (default 256, 1–4096). |
|
|
404
|
-
| `FS_MAX_INLINE_MATCHES` | Deprecated and ignored; `maxResults` sets the `search_text` page size. Logs a warning when set; removed in the next major. |
|
|
405
|
-
| `FS_MAX_READ_MANY_BYTES` | Max total bytes across a batched `read` (default 524288, 10240–104857600). |
|
|
406
|
-
| `FS_SEARCH_TIMEOUT_MS` | Search timeout in ms (default 5000, 100–60000). |
|
|
407
413
|
| `NO_COLOR` | Any value disables ANSI color output. |
|
|
408
414
|
| `FS_REQUEST_STATE_KEY` | HMAC key sealing `input_required` requestState across retry rounds. Optional (random per boot if unset); set it, at >=32 bytes UTF-8, to keep in-flight rounds alive across a restart. |
|
|
409
415
|
|
package/dist/cli-help.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-help.d.ts","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli-help.d.ts","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"AAmIA,wBAAgB,gBAAgB,IAAI,KAAK,CA4BxC;AAED,wBAAgB,mBAAmB,IAAI,KAAK,CAG3C"}
|
package/dist/cli-help.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
1
2
|
import packageJson from '../package.json' with { type: 'json' };
|
|
2
|
-
import {
|
|
3
|
-
import { MUTATING_TOOL_NAMES } from './tools/index.js';
|
|
3
|
+
import { MUTATING_TOOL_NAMES } from "./tools/index.js";
|
|
4
4
|
// Help text and its rendering. Split out of cli.ts so that module holds only
|
|
5
5
|
// argument parsing and directory validation: this file is static copy plus a
|
|
6
6
|
// formatter, and shares no state with the parser.
|
|
@@ -17,12 +17,7 @@ const OPTIONS_HELP = [
|
|
|
17
17
|
flags: '--read-only',
|
|
18
18
|
desc: `Disable write tools: ${[...MUTATING_TOOL_NAMES].sort().join(', ')}`,
|
|
19
19
|
},
|
|
20
|
-
{ flags: '--
|
|
21
|
-
{
|
|
22
|
-
flags: '--print-config',
|
|
23
|
-
desc: 'Print the active configuration and exit (use with --json for machine output)',
|
|
24
|
-
},
|
|
25
|
-
{ flags: '--json', desc: 'Output --print-config as JSON' },
|
|
20
|
+
{ flags: '--print-config', desc: 'Print the active configuration as JSON and exit' },
|
|
26
21
|
{
|
|
27
22
|
flags: '--log-level <level>',
|
|
28
23
|
desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency (env: FS_LOG_LEVEL)',
|
|
@@ -46,6 +41,10 @@ const OPTIONS_HELP = [
|
|
|
46
41
|
},
|
|
47
42
|
{ flags: '--walk-cwd', desc: 'Walk up from CWD to find a project root; implies --allow-cwd' },
|
|
48
43
|
{ flags: '--deny <pattern>', desc: 'Block paths matching this pattern; repeatable' },
|
|
44
|
+
{
|
|
45
|
+
flags: '--allow <pattern>',
|
|
46
|
+
desc: 'Exempt a pattern from the built-in sensitive denylist; repeatable (env: FS_ALLOWLIST)',
|
|
47
|
+
},
|
|
49
48
|
{
|
|
50
49
|
flags: '--allow-missing-roots',
|
|
51
50
|
desc: 'Start even if configured allowed directories do not exist',
|
|
@@ -78,6 +77,10 @@ const ENV_HELP = [
|
|
|
78
77
|
desc: 'Walk up from CWD to find a project root ("true" or "1" enables this)',
|
|
79
78
|
},
|
|
80
79
|
{ flags: 'FS_DENYLIST', desc: 'Paths/patterns to block, comma-separated' },
|
|
80
|
+
{
|
|
81
|
+
flags: 'FS_ALLOWLIST',
|
|
82
|
+
desc: 'Patterns exempted from the built-in sensitive denylist, comma-separated; --deny/FS_DENYLIST still win',
|
|
83
|
+
},
|
|
81
84
|
{
|
|
82
85
|
flags: 'FS_ALLOW_MISSING_ROOTS',
|
|
83
86
|
desc: 'Start even if configured allowed directories do not exist ("true" or "1" enables this)',
|
|
@@ -96,27 +99,10 @@ const ENV_HELP = [
|
|
|
96
99
|
flags: 'FS_RATE_LIMIT_RPM',
|
|
97
100
|
desc: 'Per-client-IP requests/min (default 120 authenticated, 6000 keyless loopback; 1–100000)',
|
|
98
101
|
},
|
|
99
|
-
{
|
|
100
|
-
flags: 'FS_MAX_REQUEST_BYTES',
|
|
101
|
-
desc: 'Max HTTP request body bytes (default 4194304, 1024–268435456)',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
flags: 'FS_KEEPALIVE_TIMEOUT_MS',
|
|
105
|
-
desc: 'HTTP keep-alive timeout; set above any fronting proxy idle timeout (default 5000, 1000–600000)',
|
|
106
|
-
},
|
|
107
102
|
{
|
|
108
103
|
flags: 'FS_MAX_WATCHERS',
|
|
109
104
|
desc: 'Max concurrent file watchers (default 256, 1–4096)',
|
|
110
105
|
},
|
|
111
|
-
{
|
|
112
|
-
flags: 'FS_MAX_INLINE_MATCHES',
|
|
113
|
-
desc: 'Deprecated and ignored; maxResults sets the search_text page size. Removed in the next major',
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
flags: 'FS_MAX_READ_MANY_BYTES',
|
|
117
|
-
desc: 'Max total bytes across one batch read (default 524288, 10240–104857600)',
|
|
118
|
-
},
|
|
119
|
-
{ flags: 'FS_SEARCH_TIMEOUT_MS', desc: 'Search timeout in ms (default 5000, 100–60000)' },
|
|
120
106
|
{ flags: 'NO_COLOR', desc: 'Any value disables ANSI color output' },
|
|
121
107
|
{
|
|
122
108
|
flags: 'FS_REQUEST_STATE_KEY',
|
|
@@ -129,31 +115,26 @@ const EXAMPLES_HELP = [
|
|
|
129
115
|
'$ filesystem-mcp /project/src /project/tests --allow-cwd',
|
|
130
116
|
'$ filesystem-mcp --port 3000 /path/to/allowed/dir',
|
|
131
117
|
'$ filesystem-mcp --read-only /data/readonly',
|
|
132
|
-
'$ filesystem-mcp --print-config
|
|
118
|
+
'$ filesystem-mcp --print-config /project',
|
|
133
119
|
];
|
|
134
120
|
export function printHelpAndExit() {
|
|
135
|
-
const { bold, dim, section, flag, placeholder, cyan } = cliFmt;
|
|
136
121
|
const COL = 27;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
};
|
|
143
|
-
const envRow = (name, desc) => {
|
|
144
|
-
return ` ${padEndVisible(cyan(name), COL - 1)} ${desc}`;
|
|
145
|
-
};
|
|
122
|
+
// Pad first, colour after: `styleText` honours NO_COLOR, and padding the
|
|
123
|
+
// plain text keeps the columns aligned whether or not colour is on.
|
|
124
|
+
const row = (color, name, desc) => ` ${styleText(color, name.padEnd(COL))}${desc}`;
|
|
125
|
+
const dim = (t) => styleText('dim', t);
|
|
126
|
+
const section = (t) => styleText(['cyan', 'bold'], t);
|
|
146
127
|
const lines = [
|
|
147
128
|
'',
|
|
148
|
-
`${
|
|
129
|
+
`${styleText('bold', 'Filesystem MCP')} ${dim(`v${SERVER_VERSION}`)}`,
|
|
149
130
|
'',
|
|
150
131
|
dim('Pass one or more directories to set the allowed access roots.'),
|
|
151
132
|
'',
|
|
152
133
|
section('Options:'),
|
|
153
|
-
...OPTIONS_HELP.map(({ flags, desc }) =>
|
|
134
|
+
...OPTIONS_HELP.map(({ flags, desc }) => row('green', flags, desc)),
|
|
154
135
|
'',
|
|
155
136
|
`${section('Environment variables:')} ${dim('(flags take precedence when both are set)')}`,
|
|
156
|
-
...ENV_HELP.map(({ flags, desc }) =>
|
|
137
|
+
...ENV_HELP.map(({ flags, desc }) => row('cyan', flags, desc)),
|
|
157
138
|
'',
|
|
158
139
|
section('Examples:'),
|
|
159
140
|
...EXAMPLES_HELP.map((ex) => ` ${dim(ex)}`),
|
|
@@ -163,7 +144,7 @@ export function printHelpAndExit() {
|
|
|
163
144
|
process.exit(0);
|
|
164
145
|
}
|
|
165
146
|
export function printVersionAndExit() {
|
|
166
|
-
process.stdout.write(`${
|
|
147
|
+
process.stdout.write(`${styleText('cyan', SERVER_VERSION)}\n`);
|
|
167
148
|
process.exit(0);
|
|
168
149
|
}
|
|
169
150
|
//# sourceMappingURL=cli-help.js.map
|
package/dist/cli-help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"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","sourcesContent":["import packageJson from '../package.json' with { type: 'json' };\nimport { cliFmt, padEndVisible } from './core/fmt.js';\nimport { MUTATING_TOOL_NAMES } from './tools/index.js';\n\n// Help text and its rendering. Split out of cli.ts so that module holds only\n// argument parsing and directory validation: this file is static copy plus a\n// formatter, and shares no state with the parser.\n\nconst { version: SERVER_VERSION } = packageJson;\n\ninterface HelpRow {\n flags: string;\n desc: string;\n}\n\nconst OPTIONS_HELP: HelpRow[] = [\n { flags: '-h, --help', desc: 'Show this help message' },\n { flags: '-v, --version', desc: 'Show the server version' },\n { flags: '--allow-cwd', desc: 'Add the current working directory as an allowed root' },\n {\n flags: '--port <number>',\n desc: 'Start HTTP transport on this port (env: FS_PORT)',\n },\n {\n flags: '--read-only',\n desc: `Disable write tools: ${[...MUTATING_TOOL_NAMES].sort().join(', ')}`,\n },\n { flags: '--safe', desc: 'Alias for --read-only' },\n {\n flags: '--print-config',\n desc: 'Print the active configuration and exit (use with --json for machine output)',\n },\n { flags: '--json', desc: 'Output --print-config as JSON' },\n {\n flags: '--log-level <level>',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency (env: FS_LOG_LEVEL)',\n },\n { flags: '--http-host <host>', desc: 'HTTP server bind address (env: FS_HTTP_HOST)' },\n {\n flags: '--api-key <key>',\n desc: 'Require this API key on HTTP requests; prefer FS_API_KEY (argv is world-readable)',\n },\n {\n flags: '--allow-sensitive',\n desc: 'Allow access to sensitive system paths (env: FS_ALLOW_SENSITIVE)',\n },\n {\n flags: '--root-boundary <path>',\n desc: 'Require all allowed roots to fall under this path (env: FS_ROOT_BOUNDARY)',\n },\n {\n flags: '--max-file-size <bytes>',\n desc: 'Maximum file size for reads in bytes (env: FS_MAX_FILE_SIZE)',\n },\n { flags: '--walk-cwd', desc: 'Walk up from CWD to find a project root; implies --allow-cwd' },\n { flags: '--deny <pattern>', desc: 'Block paths matching this pattern; repeatable' },\n {\n flags: '--allow-missing-roots',\n desc: 'Start even if configured allowed directories do not exist',\n },\n];\n\nconst ENV_HELP: HelpRow[] = [\n {\n flags: 'FS_LOG_LEVEL',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency',\n },\n { flags: 'FS_PORT', desc: 'Start HTTP transport on this port (unset = stdio)' },\n { flags: 'FS_HTTP_HOST', desc: 'HTTP bind address' },\n { flags: 'FS_API_KEY', desc: 'HTTP API key' },\n {\n flags: 'FS_TRUST_PROXY',\n desc: 'Express trust-proxy setting: hop count or expression (unset = do not trust X-Forwarded-*)',\n },\n {\n flags: 'FS_ALLOW_SENSITIVE',\n desc: 'Allow sensitive system paths (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_ROOT_BOUNDARY', desc: 'Path prefix all allowed roots must fall under' },\n { flags: 'FS_MAX_FILE_SIZE', desc: 'Maximum file size for reads in bytes' },\n {\n flags: 'FS_ALLOWED_DIRS',\n desc: 'Allowed dirs: colon-separated (Unix), semicolon-separated (Windows)',\n },\n {\n flags: 'FS_ALLOW_CWD_WALK',\n desc: 'Walk up from CWD to find a project root (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_DENYLIST', desc: 'Paths/patterns to block, comma-separated' },\n {\n flags: 'FS_ALLOW_MISSING_ROOTS',\n desc: 'Start even if configured allowed directories do not exist (\"true\" or \"1\" enables this)',\n },\n {\n flags: 'FS_ALLOWED_HOSTS',\n desc: 'Comma-separated Host header values to accept (HTTP transport)',\n },\n { flags: 'FS_ALLOWED_ORIGINS', desc: 'Comma-separated origin hostnames for CORS' },\n {\n flags: 'FS_ALLOW_UNRESTRICTED_HOSTS',\n desc: 'Bind a wildcard host with no Host validation, accepts the risk (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_PUBLIC_URL', desc: 'Resource identifier URL for RFC 9728 discovery' },\n {\n flags: 'FS_RATE_LIMIT_RPM',\n desc: 'Per-client-IP requests/min (default 120 authenticated, 6000 keyless loopback; 1–100000)',\n },\n {\n flags: 'FS_MAX_REQUEST_BYTES',\n desc: 'Max HTTP request body bytes (default 4194304, 1024–268435456)',\n },\n {\n flags: 'FS_KEEPALIVE_TIMEOUT_MS',\n desc: 'HTTP keep-alive timeout; set above any fronting proxy idle timeout (default 5000, 1000–600000)',\n },\n {\n flags: 'FS_MAX_WATCHERS',\n desc: 'Max concurrent file watchers (default 256, 1–4096)',\n },\n {\n flags: 'FS_MAX_INLINE_MATCHES',\n desc: 'Deprecated and ignored; maxResults sets the search_text page size. Removed in the next major',\n },\n {\n flags: 'FS_MAX_READ_MANY_BYTES',\n desc: 'Max total bytes across one batch read (default 524288, 10240–104857600)',\n },\n { flags: 'FS_SEARCH_TIMEOUT_MS', desc: 'Search timeout in ms (default 5000, 100–60000)' },\n { flags: 'NO_COLOR', desc: 'Any value disables ANSI color output' },\n {\n flags: 'FS_REQUEST_STATE_KEY',\n desc: 'HMAC key for input_required state; optional outside fleet mode, shared and >=32 bytes in fleet mode',\n },\n];\n\nconst EXAMPLES_HELP = [\n '$ filesystem-mcp /path/to/allowed/dir',\n '$ filesystem-mcp --allow-cwd',\n '$ filesystem-mcp /project/src /project/tests --allow-cwd',\n '$ filesystem-mcp --port 3000 /path/to/allowed/dir',\n '$ filesystem-mcp --read-only /data/readonly',\n '$ filesystem-mcp --print-config --json /project',\n];\n\nexport function printHelpAndExit(): never {\n const { bold, dim, section, flag, placeholder, cyan } = cliFmt;\n const COL = 27;\n\n const optRow = (flags: string, desc: string): string => {\n const colored = flags\n .replace(/<[^>]+>/g, (m) => placeholder(m))\n .replace(/-{1,2}[\\w-]+/g, (m) => flag(m));\n return ` ${padEndVisible(colored, COL)}${desc}`;\n };\n\n const envRow = (name: string, desc: string): string => {\n return ` ${padEndVisible(cyan(name), COL - 1)} ${desc}`;\n };\n\n const lines = [\n '',\n `${bold('Filesystem MCP')} ${dim(`v${SERVER_VERSION}`)}`,\n '',\n dim('Pass one or more directories to set the allowed access roots.'),\n '',\n section('Options:'),\n ...OPTIONS_HELP.map(({ flags, desc }) => optRow(flags, desc)),\n '',\n `${section('Environment variables:')} ${dim('(flags take precedence when both are set)')}`,\n ...ENV_HELP.map(({ flags, desc }) => envRow(flags, desc)),\n '',\n section('Examples:'),\n ...EXAMPLES_HELP.map((ex) => ` ${dim(ex)}`),\n '',\n ];\n\n process.stdout.write(lines.join('\\n'));\n process.exit(0);\n}\n\nexport function printVersionAndExit(): never {\n process.stdout.write(`${cliFmt.cyan(SERVER_VERSION)}\\n`);\n process.exit(0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"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","sourcesContent":["import { styleText } from 'node:util';\n\nimport packageJson from '../package.json' with { type: 'json' };\nimport { MUTATING_TOOL_NAMES } from './tools/index.ts';\n\n// Help text and its rendering. Split out of cli.ts so that module holds only\n// argument parsing and directory validation: this file is static copy plus a\n// formatter, and shares no state with the parser.\n\nconst { version: SERVER_VERSION } = packageJson;\n\ninterface HelpRow {\n flags: string;\n desc: string;\n}\n\nconst OPTIONS_HELP: HelpRow[] = [\n { flags: '-h, --help', desc: 'Show this help message' },\n { flags: '-v, --version', desc: 'Show the server version' },\n { flags: '--allow-cwd', desc: 'Add the current working directory as an allowed root' },\n {\n flags: '--port <number>',\n desc: 'Start HTTP transport on this port (env: FS_PORT)',\n },\n {\n flags: '--read-only',\n desc: `Disable write tools: ${[...MUTATING_TOOL_NAMES].sort().join(', ')}`,\n },\n { flags: '--print-config', desc: 'Print the active configuration as JSON and exit' },\n {\n flags: '--log-level <level>',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency (env: FS_LOG_LEVEL)',\n },\n { flags: '--http-host <host>', desc: 'HTTP server bind address (env: FS_HTTP_HOST)' },\n {\n flags: '--api-key <key>',\n desc: 'Require this API key on HTTP requests; prefer FS_API_KEY (argv is world-readable)',\n },\n {\n flags: '--allow-sensitive',\n desc: 'Allow access to sensitive system paths (env: FS_ALLOW_SENSITIVE)',\n },\n {\n flags: '--root-boundary <path>',\n desc: 'Require all allowed roots to fall under this path (env: FS_ROOT_BOUNDARY)',\n },\n {\n flags: '--max-file-size <bytes>',\n desc: 'Maximum file size for reads in bytes (env: FS_MAX_FILE_SIZE)',\n },\n { flags: '--walk-cwd', desc: 'Walk up from CWD to find a project root; implies --allow-cwd' },\n { flags: '--deny <pattern>', desc: 'Block paths matching this pattern; repeatable' },\n {\n flags: '--allow <pattern>',\n desc: 'Exempt a pattern from the built-in sensitive denylist; repeatable (env: FS_ALLOWLIST)',\n },\n {\n flags: '--allow-missing-roots',\n desc: 'Start even if configured allowed directories do not exist',\n },\n];\n\nconst ENV_HELP: HelpRow[] = [\n {\n flags: 'FS_LOG_LEVEL',\n desc: 'Log level, RFC 5424: debug|info|notice|warn|error|critical|alert|emergency',\n },\n { flags: 'FS_PORT', desc: 'Start HTTP transport on this port (unset = stdio)' },\n { flags: 'FS_HTTP_HOST', desc: 'HTTP bind address' },\n { flags: 'FS_API_KEY', desc: 'HTTP API key' },\n {\n flags: 'FS_TRUST_PROXY',\n desc: 'Express trust-proxy setting: hop count or expression (unset = do not trust X-Forwarded-*)',\n },\n {\n flags: 'FS_ALLOW_SENSITIVE',\n desc: 'Allow sensitive system paths (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_ROOT_BOUNDARY', desc: 'Path prefix all allowed roots must fall under' },\n { flags: 'FS_MAX_FILE_SIZE', desc: 'Maximum file size for reads in bytes' },\n {\n flags: 'FS_ALLOWED_DIRS',\n desc: 'Allowed dirs: colon-separated (Unix), semicolon-separated (Windows)',\n },\n {\n flags: 'FS_ALLOW_CWD_WALK',\n desc: 'Walk up from CWD to find a project root (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_DENYLIST', desc: 'Paths/patterns to block, comma-separated' },\n {\n flags: 'FS_ALLOWLIST',\n desc: 'Patterns exempted from the built-in sensitive denylist, comma-separated; --deny/FS_DENYLIST still win',\n },\n {\n flags: 'FS_ALLOW_MISSING_ROOTS',\n desc: 'Start even if configured allowed directories do not exist (\"true\" or \"1\" enables this)',\n },\n {\n flags: 'FS_ALLOWED_HOSTS',\n desc: 'Comma-separated Host header values to accept (HTTP transport)',\n },\n { flags: 'FS_ALLOWED_ORIGINS', desc: 'Comma-separated origin hostnames for CORS' },\n {\n flags: 'FS_ALLOW_UNRESTRICTED_HOSTS',\n desc: 'Bind a wildcard host with no Host validation, accepts the risk (\"true\" or \"1\" enables this)',\n },\n { flags: 'FS_PUBLIC_URL', desc: 'Resource identifier URL for RFC 9728 discovery' },\n {\n flags: 'FS_RATE_LIMIT_RPM',\n desc: 'Per-client-IP requests/min (default 120 authenticated, 6000 keyless loopback; 1–100000)',\n },\n {\n flags: 'FS_MAX_WATCHERS',\n desc: 'Max concurrent file watchers (default 256, 1–4096)',\n },\n { flags: 'NO_COLOR', desc: 'Any value disables ANSI color output' },\n {\n flags: 'FS_REQUEST_STATE_KEY',\n desc: 'HMAC key for input_required state; optional outside fleet mode, shared and >=32 bytes in fleet mode',\n },\n];\n\nconst EXAMPLES_HELP = [\n '$ filesystem-mcp /path/to/allowed/dir',\n '$ filesystem-mcp --allow-cwd',\n '$ filesystem-mcp /project/src /project/tests --allow-cwd',\n '$ filesystem-mcp --port 3000 /path/to/allowed/dir',\n '$ filesystem-mcp --read-only /data/readonly',\n '$ filesystem-mcp --print-config /project',\n];\n\nexport function printHelpAndExit(): never {\n const COL = 27;\n // Pad first, colour after: `styleText` honours NO_COLOR, and padding the\n // plain text keeps the columns aligned whether or not colour is on.\n const row = (color: 'green' | 'cyan', name: string, desc: string): string =>\n ` ${styleText(color, name.padEnd(COL))}${desc}`;\n const dim = (t: string): string => styleText('dim', t);\n const section = (t: string): string => styleText(['cyan', 'bold'], t);\n\n const lines = [\n '',\n `${styleText('bold', 'Filesystem MCP')} ${dim(`v${SERVER_VERSION}`)}`,\n '',\n dim('Pass one or more directories to set the allowed access roots.'),\n '',\n section('Options:'),\n ...OPTIONS_HELP.map(({ flags, desc }) => row('green', flags, desc)),\n '',\n `${section('Environment variables:')} ${dim('(flags take precedence when both are set)')}`,\n ...ENV_HELP.map(({ flags, desc }) => row('cyan', flags, desc)),\n '',\n section('Examples:'),\n ...EXAMPLES_HELP.map((ex) => ` ${dim(ex)}`),\n '',\n ];\n\n process.stdout.write(lines.join('\\n'));\n process.exit(0);\n}\n\nexport function printVersionAndExit(): never {\n process.stdout.write(`${styleText('cyan', SERVER_VERSION)}\\n`);\n process.exit(0);\n}\n"]}
|
package/dist/cli.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export declare function parseArgs(): Promise<{
|
|
|
7
7
|
port: number | undefined;
|
|
8
8
|
readOnly: boolean;
|
|
9
9
|
printConfig: boolean;
|
|
10
|
-
json: boolean;
|
|
11
10
|
httpHost: string | undefined;
|
|
12
11
|
apiKey: string | undefined;
|
|
13
12
|
}>;
|
|
@@ -15,7 +14,6 @@ export declare function runPrintConfig(options: {
|
|
|
15
14
|
allowedDirs: string[];
|
|
16
15
|
allowCwd: boolean;
|
|
17
16
|
readOnly: boolean;
|
|
18
|
-
json: boolean;
|
|
19
17
|
/** Resolved `--port`. Present means the launch this reports on is an HTTP bind. */
|
|
20
18
|
port?: number;
|
|
21
19
|
httpHost?: string;
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAqBA,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAyFD,wBAAsB,SAAS,IAAI,OAAO,CAAC;IACzC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,CAAC,CA2ED;AAMD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBhB"}
|
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { stat } from 'node:fs/promises';
|
|
2
2
|
import { parseArgs as utilParseArgs } from 'node:util';
|
|
3
|
-
import { printHelpAndExit, printVersionAndExit } from
|
|
4
|
-
import { cli } from
|
|
5
|
-
import { formatUnknownErrorMessage } from
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { getMaxTextFileSize } from './core/util.js';
|
|
11
|
-
import { registeredTools } from './tools/index.js';
|
|
3
|
+
import { printHelpAndExit, printVersionAndExit } from "./cli-help.js";
|
|
4
|
+
import { cli } from "./core/config.js";
|
|
5
|
+
import { formatUnknownErrorMessage } from "./core/errors.js";
|
|
6
|
+
import { getReservedDeviceNameForPath, isWindowsDriveRelativePath, normalizePath, parseTrueEnvFlag, } from "./core/path-utils.js";
|
|
7
|
+
import { PathGuard } from "./core/path.js";
|
|
8
|
+
import { getMaxTextFileSize } from "./core/util.js";
|
|
9
|
+
import { registeredTools } from "./tools/index.js";
|
|
12
10
|
// ════════════════════════════════════════════════════════════
|
|
13
11
|
// Path & Config Utilities — pure functions and error types
|
|
14
12
|
// ════════════════════════════════════════════════════════════
|
|
@@ -48,29 +46,20 @@ async function validateDirectoryPath(inputPath, allowMissing = false) {
|
|
|
48
46
|
throw new Error(`${inputPath} is not a directory`);
|
|
49
47
|
return normalized;
|
|
50
48
|
}
|
|
49
|
+
// No dedupe here: PathGuard's `normalizeAllowedDirectories` already Set-dedupes
|
|
50
|
+
// the normalized set, and containment checks are case-insensitive where the
|
|
51
|
+
// filesystem is.
|
|
51
52
|
async function normalizeAndValidateDirs(paths, allowMissing = false) {
|
|
52
53
|
const normalized = [];
|
|
53
54
|
for (const p of paths) {
|
|
54
55
|
normalized.push(await validateDirectoryPath(p, allowMissing));
|
|
55
56
|
}
|
|
56
|
-
return
|
|
57
|
+
return normalized;
|
|
57
58
|
}
|
|
58
59
|
function normalizeCliExitMessage(error) {
|
|
59
60
|
const rawMessage = formatUnknownErrorMessage(error);
|
|
60
61
|
return rawMessage.startsWith('Error:') ? rawMessage : `Error: ${rawMessage}`;
|
|
61
62
|
}
|
|
62
|
-
function deduplicateAllowedDirectories(dirs) {
|
|
63
|
-
const seen = new Set();
|
|
64
|
-
const deduplicated = [];
|
|
65
|
-
for (const dir of dirs) {
|
|
66
|
-
const key = IS_WINDOWS ? dir.toLowerCase() : dir;
|
|
67
|
-
if (seen.has(key))
|
|
68
|
-
continue;
|
|
69
|
-
seen.add(key);
|
|
70
|
-
deduplicated.push(dir);
|
|
71
|
-
}
|
|
72
|
-
return deduplicated;
|
|
73
|
-
}
|
|
74
63
|
function parsePortOption(raw) {
|
|
75
64
|
// '' reads as unset: FS_PORT set-but-empty (compose templating) means stdio.
|
|
76
65
|
if (raw === undefined || raw === '')
|
|
@@ -85,9 +74,7 @@ const CLI_PARSER_CONFIG = {
|
|
|
85
74
|
options: {
|
|
86
75
|
'allow-cwd': { type: 'boolean', default: false },
|
|
87
76
|
'read-only': { type: 'boolean', default: false },
|
|
88
|
-
safe: { type: 'boolean', default: false },
|
|
89
77
|
'print-config': { type: 'boolean', default: false },
|
|
90
|
-
json: { type: 'boolean', default: false },
|
|
91
78
|
port: { type: 'string' },
|
|
92
79
|
help: { type: 'boolean', short: 'h' },
|
|
93
80
|
version: { type: 'boolean', short: 'v' },
|
|
@@ -99,6 +86,7 @@ const CLI_PARSER_CONFIG = {
|
|
|
99
86
|
'max-file-size': { type: 'string' },
|
|
100
87
|
'walk-cwd': { type: 'boolean', default: false },
|
|
101
88
|
deny: { type: 'string', multiple: true },
|
|
89
|
+
allow: { type: 'string', multiple: true },
|
|
102
90
|
'allow-missing-roots': { type: 'boolean', default: false },
|
|
103
91
|
},
|
|
104
92
|
strict: true,
|
|
@@ -136,17 +124,20 @@ export async function parseArgs() {
|
|
|
136
124
|
cli.allowCwdWalk = true;
|
|
137
125
|
if (v['allow-missing-roots'])
|
|
138
126
|
cli.allowMissingRoots = true;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
127
|
+
const patternList = (entries) => [
|
|
128
|
+
...new Set((entries ?? []).map((entry) => entry.trim()).filter(Boolean)),
|
|
129
|
+
];
|
|
130
|
+
const denyPatterns = patternList(v.deny);
|
|
131
|
+
if (denyPatterns.length > 0)
|
|
132
|
+
cli.denyPatterns = denyPatterns;
|
|
133
|
+
const allowPatterns = patternList(v.allow);
|
|
134
|
+
if (allowPatterns.length > 0)
|
|
135
|
+
cli.allowPatterns = allowPatterns;
|
|
144
136
|
const allowCwd = v['allow-cwd'] ||
|
|
145
137
|
v['walk-cwd'] ||
|
|
146
138
|
parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');
|
|
147
|
-
const readOnly = v['read-only']
|
|
139
|
+
const readOnly = v['read-only'];
|
|
148
140
|
const printConfig = v['print-config'];
|
|
149
|
-
const json = v.json;
|
|
150
141
|
const port = parsePortOption(v.port ?? process.env['FS_PORT']);
|
|
151
142
|
const allowMissingRoots = v['allow-missing-roots'] ||
|
|
152
143
|
parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');
|
|
@@ -166,7 +157,6 @@ export async function parseArgs() {
|
|
|
166
157
|
port,
|
|
167
158
|
readOnly,
|
|
168
159
|
printConfig,
|
|
169
|
-
json,
|
|
170
160
|
httpHost,
|
|
171
161
|
apiKey,
|
|
172
162
|
};
|
|
@@ -179,10 +169,9 @@ export async function parseArgs() {
|
|
|
179
169
|
}
|
|
180
170
|
}
|
|
181
171
|
// ════════════════════════════════════════════════════════════
|
|
182
|
-
// Effective config reporting — prints the resolved server config
|
|
172
|
+
// Effective config reporting — prints the resolved server config as JSON
|
|
183
173
|
// ════════════════════════════════════════════════════════════
|
|
184
174
|
export async function runPrintConfig(options) {
|
|
185
|
-
const write = (s) => process.stdout.write(s);
|
|
186
175
|
const pathGuard = new PathGuard({
|
|
187
176
|
allowCwd: options.allowCwd,
|
|
188
177
|
cliAllowedDirs: options.allowedDirs,
|
|
@@ -202,17 +191,6 @@ export async function runPrintConfig(options) {
|
|
|
202
191
|
apiKey: options.apiKey ? '***' : null,
|
|
203
192
|
limits: { maxFileSizeBytes: getMaxTextFileSize() },
|
|
204
193
|
};
|
|
205
|
-
|
|
206
|
-
write(JSON.stringify(config, null, 2));
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
const key = (k) => padEndVisible(cliFmt.cyan(k), 14);
|
|
210
|
-
write(`${key('transport:')}${config.transport}\n`);
|
|
211
|
-
write(`${key('readOnly:')}${cliFmt.bool(config.readOnly)}\n`);
|
|
212
|
-
write(`${key('apiKey:')}${cliFmt.dim(config.apiKey ?? 'none')}\n`);
|
|
213
|
-
write(`${key('allowedRoots:')}${config.allowedRoots.join(', ') || cliFmt.dim('(none)')}\n`);
|
|
214
|
-
write(`${key('tools:')}${cliFmt.dim(config.tools.join(', '))}\n`);
|
|
215
|
-
write(`${key('maxFileSize:')}${cliFmt.yellow(String(config.limits.maxFileSizeBytes))}\n`);
|
|
216
|
-
}
|
|
194
|
+
process.stdout.write(`${JSON.stringify(config, null, 2)}\n`);
|
|
217
195
|
}
|
|
218
196
|
//# sourceMappingURL=cli.js.map
|
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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","sourcesContent":["import type { Stats } from 'node:fs';\nimport { stat } from 'node:fs/promises';\nimport { parseArgs as utilParseArgs } from 'node:util';\n\nimport { printHelpAndExit, printVersionAndExit } from './cli-help.js';\nimport { cli } from './core/config.js';\nimport { formatUnknownErrorMessage } from './core/errors.js';\nimport { cliFmt, padEndVisible } from './core/fmt.js';\nimport {\n getReservedDeviceNameForPath,\n isWindowsDriveRelativePath,\n normalizePath,\n} from './core/path-utils.js';\nimport { PathGuard } from './core/path.js';\nimport { IS_WINDOWS, parseTrueEnvFlag } from './core/primitives.js';\nimport { getMaxTextFileSize } from './core/util.js';\nimport { registeredTools } from './tools/index.js';\n\n// ════════════════════════════════════════════════════════════\n// Path & Config Utilities — pure functions and error types\n// ════════════════════════════════════════════════════════════\n\nexport class CliExitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CliExitError';\n }\n}\n\nfunction validateCliPath(inputPath: string): void {\n if (inputPath.includes('\\0')) {\n throw new CliExitError('Path contains null bytes.');\n }\n\n if (isWindowsDriveRelativePath(inputPath)) {\n throw new CliExitError(\n 'Windows drive-relative paths are not allowed. Use C:\\\\path or C:/path instead of C:path.',\n );\n }\n\n const reserved = getReservedDeviceNameForPath(inputPath);\n if (reserved) {\n throw new CliExitError(`Windows reserved device name not allowed: ${reserved}.`);\n }\n}\n\nasync function validateDirectoryPath(inputPath: string, allowMissing = false): Promise<string> {\n const normalized = normalizePath(inputPath);\n\n let stats: Stats;\n try {\n stats = await stat(normalized);\n } catch (error) {\n if (allowMissing) return normalized;\n // Node's own message already names the syscall and the errno.\n throw new Error(`Cannot access directory ${inputPath}: ${formatUnknownErrorMessage(error)}`, {\n cause: error,\n });\n }\n\n if (!stats.isDirectory()) throw new Error(`${inputPath} is not a directory`);\n return normalized;\n}\n\nasync function normalizeAndValidateDirs(\n paths: readonly string[],\n allowMissing = false,\n): Promise<string[]> {\n const normalized: string[] = [];\n for (const p of paths) {\n normalized.push(await validateDirectoryPath(p, allowMissing));\n }\n return deduplicateAllowedDirectories(normalized);\n}\n\nfunction normalizeCliExitMessage(error: unknown): string {\n const rawMessage = formatUnknownErrorMessage(error);\n return rawMessage.startsWith('Error:') ? rawMessage : `Error: ${rawMessage}`;\n}\n\nfunction deduplicateAllowedDirectories(dirs: readonly string[]): string[] {\n const seen = new Set<string>();\n const deduplicated: string[] = [];\n\n for (const dir of dirs) {\n const key = IS_WINDOWS ? dir.toLowerCase() : dir;\n if (seen.has(key)) continue;\n seen.add(key);\n deduplicated.push(dir);\n }\n\n return deduplicated;\n}\n\nfunction parsePortOption(raw: unknown): number | undefined {\n // '' reads as unset: FS_PORT set-but-empty (compose templating) means stdio.\n if (raw === undefined || raw === '') return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new CliExitError(`Error: --port / FS_PORT must be an integer between 1 and 65535`);\n }\n return n;\n}\n\nconst CLI_PARSER_CONFIG = {\n options: {\n 'allow-cwd': { type: 'boolean', default: false },\n 'read-only': { type: 'boolean', default: false },\n safe: { type: 'boolean', default: false },\n 'print-config': { type: 'boolean', default: false },\n json: { type: 'boolean', default: false },\n port: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n 'log-level': { type: 'string' },\n 'http-host': { type: 'string' },\n 'api-key': { type: 'string' },\n 'allow-sensitive': { type: 'boolean', default: false },\n 'root-boundary': { type: 'string' },\n 'max-file-size': { type: 'string' },\n 'walk-cwd': { type: 'boolean', default: false },\n deny: { type: 'string', multiple: true },\n 'allow-missing-roots': { type: 'boolean', default: false },\n },\n strict: true,\n allowPositionals: true,\n} as const;\n\nexport async function parseArgs(): Promise<{\n allowedDirs: string[];\n allowCwd: boolean;\n port: number | undefined;\n readOnly: boolean;\n printConfig: boolean;\n json: boolean;\n httpHost: string | undefined;\n apiKey: string | undefined;\n}> {\n try {\n const parsed = utilParseArgs(CLI_PARSER_CONFIG);\n\n if (parsed.values.help) {\n printHelpAndExit();\n }\n\n if (parsed.values.version) {\n printVersionAndExit();\n }\n\n for (const positional of parsed.positionals) {\n validateCliPath(positional);\n }\n\n const v = parsed.values;\n // `--http-host` and `--api-key` are returned to the caller and handed to\n // the server as config. Every other flag lands in the CLI-override store\n // (core/config.ts) — the one owner of the flag-beats-env rule — which deep\n // core readers (path, sensitive, observability, util) consult before\n // falling back to the operator's environment. Nothing writes process.env.\n const httpHost = v['http-host'] ?? process.env['FS_HTTP_HOST'];\n const apiKey = v['api-key'] ?? process.env['FS_API_KEY'];\n if (v['log-level'] !== undefined) cli.logLevel = v['log-level'];\n if (v['max-file-size'] !== undefined) cli.maxFileSize = v['max-file-size'];\n if (v['root-boundary'] !== undefined) cli.rootBoundary = v['root-boundary'];\n if (v['allow-sensitive']) cli.allowSensitive = true;\n if (v['walk-cwd']) cli.allowCwdWalk = true;\n if (v['allow-missing-roots']) cli.allowMissingRoots = true;\n if (v.deny !== undefined) {\n const denyPatterns = [...new Set(v.deny.map((entry) => entry.trim()).filter(Boolean))];\n if (denyPatterns.length > 0) cli.denyPatterns = denyPatterns;\n }\n\n const allowCwd =\n v['allow-cwd'] ||\n v['walk-cwd'] ||\n parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n const readOnly = v['read-only'] || v.safe;\n const printConfig = v['print-config'];\n const json = v.json;\n const port = parsePortOption(v.port ?? process.env['FS_PORT']);\n const allowMissingRoots =\n v['allow-missing-roots'] ||\n parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');\n\n let allowedDirs: string[] = [];\n try {\n allowedDirs =\n parsed.positionals.length > 0\n ? await normalizeAndValidateDirs(parsed.positionals, allowMissingRoots)\n : [];\n } catch (error: unknown) {\n throw new CliExitError(normalizeCliExitMessage(error));\n }\n\n return {\n allowedDirs,\n allowCwd,\n port,\n readOnly,\n printConfig,\n json,\n httpHost,\n apiKey,\n };\n } catch (error: unknown) {\n if (error instanceof CliExitError) {\n throw error;\n }\n\n throw new CliExitError(normalizeCliExitMessage(error));\n }\n}\n\n// ════════════════════════════════════════════════════════════\n// Effective config reporting — prints the resolved server config\n// ════════════════════════════════════════════════════════════\n\nexport async function runPrintConfig(options: {\n allowedDirs: string[];\n allowCwd: boolean;\n readOnly: boolean;\n json: boolean;\n /** Resolved `--port`. Present means the launch this reports on is an HTTP bind. */\n port?: number;\n httpHost?: string;\n apiKey?: string;\n}): Promise<void> {\n const write = (s: string) => process.stdout.write(s);\n\n const pathGuard = new PathGuard({\n allowCwd: options.allowCwd,\n cliAllowedDirs: options.allowedDirs,\n });\n await pathGuard.recomputeAllowedDirectories();\n const allowedRoots = pathGuard.getAllowedDirectories();\n\n const tools = registeredTools(options.readOnly).map((t) => t.name);\n\n // Derived, never assumed: `--print-config --port 3000` reports the HTTP bind\n // that `--port` would actually have started, not the stdio default.\n const config = {\n transport:\n options.port !== undefined\n ? `http://${options.httpHost ?? '127.0.0.1'}:${String(options.port)}`\n : 'stdio',\n readOnly: options.readOnly,\n allowedRoots,\n tools,\n apiKey: options.apiKey ? '***' : null,\n limits: { maxFileSizeBytes: getMaxTextFileSize() },\n };\n\n if (options.json) {\n write(JSON.stringify(config, null, 2));\n } else {\n const key = (k: string) => padEndVisible(cliFmt.cyan(k), 14);\n write(`${key('transport:')}${config.transport}\\n`);\n write(`${key('readOnly:')}${cliFmt.bool(config.readOnly)}\\n`);\n write(`${key('apiKey:')}${cliFmt.dim(config.apiKey ?? 'none')}\\n`);\n write(`${key('allowedRoots:')}${config.allowedRoots.join(', ') || cliFmt.dim('(none)')}\\n`);\n write(`${key('tools:')}${cliFmt.dim(config.tools.join(', '))}\\n`);\n write(`${key('maxFileSize:')}${cliFmt.yellow(String(config.limits.maxFileSizeBytes))}\\n`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Stats } from 'node:fs';\nimport { stat } from 'node:fs/promises';\nimport { parseArgs as utilParseArgs } from 'node:util';\n\nimport { printHelpAndExit, printVersionAndExit } from './cli-help.ts';\nimport { cli } from './core/config.ts';\nimport { formatUnknownErrorMessage } from './core/errors.ts';\nimport {\n getReservedDeviceNameForPath,\n isWindowsDriveRelativePath,\n normalizePath,\n parseTrueEnvFlag,\n} from './core/path-utils.ts';\nimport { PathGuard } from './core/path.ts';\nimport { getMaxTextFileSize } from './core/util.ts';\nimport { registeredTools } from './tools/index.ts';\n\n// ════════════════════════════════════════════════════════════\n// Path & Config Utilities — pure functions and error types\n// ════════════════════════════════════════════════════════════\n\nexport class CliExitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CliExitError';\n }\n}\n\nfunction validateCliPath(inputPath: string): void {\n if (inputPath.includes('\\0')) {\n throw new CliExitError('Path contains null bytes.');\n }\n\n if (isWindowsDriveRelativePath(inputPath)) {\n throw new CliExitError(\n 'Windows drive-relative paths are not allowed. Use C:\\\\path or C:/path instead of C:path.',\n );\n }\n\n const reserved = getReservedDeviceNameForPath(inputPath);\n if (reserved) {\n throw new CliExitError(`Windows reserved device name not allowed: ${reserved}.`);\n }\n}\n\nasync function validateDirectoryPath(inputPath: string, allowMissing = false): Promise<string> {\n const normalized = normalizePath(inputPath);\n\n let stats: Stats;\n try {\n stats = await stat(normalized);\n } catch (error) {\n if (allowMissing) return normalized;\n // Node's own message already names the syscall and the errno.\n throw new Error(`Cannot access directory ${inputPath}: ${formatUnknownErrorMessage(error)}`, {\n cause: error,\n });\n }\n\n if (!stats.isDirectory()) throw new Error(`${inputPath} is not a directory`);\n return normalized;\n}\n\n// No dedupe here: PathGuard's `normalizeAllowedDirectories` already Set-dedupes\n// the normalized set, and containment checks are case-insensitive where the\n// filesystem is.\nasync function normalizeAndValidateDirs(\n paths: readonly string[],\n allowMissing = false,\n): Promise<string[]> {\n const normalized: string[] = [];\n for (const p of paths) {\n normalized.push(await validateDirectoryPath(p, allowMissing));\n }\n return normalized;\n}\n\nfunction normalizeCliExitMessage(error: unknown): string {\n const rawMessage = formatUnknownErrorMessage(error);\n return rawMessage.startsWith('Error:') ? rawMessage : `Error: ${rawMessage}`;\n}\n\nfunction parsePortOption(raw: unknown): number | undefined {\n // '' reads as unset: FS_PORT set-but-empty (compose templating) means stdio.\n if (raw === undefined || raw === '') return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new CliExitError(`Error: --port / FS_PORT must be an integer between 1 and 65535`);\n }\n return n;\n}\n\nconst CLI_PARSER_CONFIG = {\n options: {\n 'allow-cwd': { type: 'boolean', default: false },\n 'read-only': { type: 'boolean', default: false },\n 'print-config': { type: 'boolean', default: false },\n port: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n version: { type: 'boolean', short: 'v' },\n 'log-level': { type: 'string' },\n 'http-host': { type: 'string' },\n 'api-key': { type: 'string' },\n 'allow-sensitive': { type: 'boolean', default: false },\n 'root-boundary': { type: 'string' },\n 'max-file-size': { type: 'string' },\n 'walk-cwd': { type: 'boolean', default: false },\n deny: { type: 'string', multiple: true },\n allow: { type: 'string', multiple: true },\n 'allow-missing-roots': { type: 'boolean', default: false },\n },\n strict: true,\n allowPositionals: true,\n} as const;\n\nexport async function parseArgs(): Promise<{\n allowedDirs: string[];\n allowCwd: boolean;\n port: number | undefined;\n readOnly: boolean;\n printConfig: boolean;\n httpHost: string | undefined;\n apiKey: string | undefined;\n}> {\n try {\n const parsed = utilParseArgs(CLI_PARSER_CONFIG);\n\n if (parsed.values.help) {\n printHelpAndExit();\n }\n\n if (parsed.values.version) {\n printVersionAndExit();\n }\n\n for (const positional of parsed.positionals) {\n validateCliPath(positional);\n }\n\n const v = parsed.values;\n // `--http-host` and `--api-key` are returned to the caller and handed to\n // the server as config. Every other flag lands in the CLI-override store\n // (core/config.ts) — the one owner of the flag-beats-env rule — which deep\n // core readers (path, sensitive, observability, util) consult before\n // falling back to the operator's environment. Nothing writes process.env.\n const httpHost = v['http-host'] ?? process.env['FS_HTTP_HOST'];\n const apiKey = v['api-key'] ?? process.env['FS_API_KEY'];\n if (v['log-level'] !== undefined) cli.logLevel = v['log-level'];\n if (v['max-file-size'] !== undefined) cli.maxFileSize = v['max-file-size'];\n if (v['root-boundary'] !== undefined) cli.rootBoundary = v['root-boundary'];\n if (v['allow-sensitive']) cli.allowSensitive = true;\n if (v['walk-cwd']) cli.allowCwdWalk = true;\n if (v['allow-missing-roots']) cli.allowMissingRoots = true;\n const patternList = (entries: readonly string[] | undefined): readonly string[] => [\n ...new Set((entries ?? []).map((entry) => entry.trim()).filter(Boolean)),\n ];\n const denyPatterns = patternList(v.deny);\n if (denyPatterns.length > 0) cli.denyPatterns = denyPatterns;\n const allowPatterns = patternList(v.allow);\n if (allowPatterns.length > 0) cli.allowPatterns = allowPatterns;\n\n const allowCwd =\n v['allow-cwd'] ||\n v['walk-cwd'] ||\n parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n const readOnly = v['read-only'];\n const printConfig = v['print-config'];\n const port = parsePortOption(v.port ?? process.env['FS_PORT']);\n const allowMissingRoots =\n v['allow-missing-roots'] ||\n parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');\n\n let allowedDirs: string[] = [];\n try {\n allowedDirs =\n parsed.positionals.length > 0\n ? await normalizeAndValidateDirs(parsed.positionals, allowMissingRoots)\n : [];\n } catch (error: unknown) {\n throw new CliExitError(normalizeCliExitMessage(error));\n }\n\n return {\n allowedDirs,\n allowCwd,\n port,\n readOnly,\n printConfig,\n httpHost,\n apiKey,\n };\n } catch (error: unknown) {\n if (error instanceof CliExitError) {\n throw error;\n }\n\n throw new CliExitError(normalizeCliExitMessage(error));\n }\n}\n\n// ════════════════════════════════════════════════════════════\n// Effective config reporting — prints the resolved server config as JSON\n// ════════════════════════════════════════════════════════════\n\nexport async function runPrintConfig(options: {\n allowedDirs: string[];\n allowCwd: boolean;\n readOnly: boolean;\n /** Resolved `--port`. Present means the launch this reports on is an HTTP bind. */\n port?: number;\n httpHost?: string;\n apiKey?: string;\n}): Promise<void> {\n const pathGuard = new PathGuard({\n allowCwd: options.allowCwd,\n cliAllowedDirs: options.allowedDirs,\n });\n await pathGuard.recomputeAllowedDirectories();\n const allowedRoots = pathGuard.getAllowedDirectories();\n\n const tools = registeredTools(options.readOnly).map((t) => t.name);\n\n // Derived, never assumed: `--print-config --port 3000` reports the HTTP bind\n // that `--port` would actually have started, not the stdio default.\n const config = {\n transport:\n options.port !== undefined\n ? `http://${options.httpHost ?? '127.0.0.1'}:${String(options.port)}`\n : 'stdio',\n readOnly: options.readOnly,\n allowedRoots,\n tools,\n apiKey: options.apiKey ? '***' : null,\n limits: { maxFileSizeBytes: getMaxTextFileSize() },\n };\n\n process.stdout.write(`${JSON.stringify(config, null, 2)}\\n`);\n}\n"]}
|
|
@@ -2,8 +2,8 @@ import * as z from 'zod/v4';
|
|
|
2
2
|
export type StoppedReason = 'maxResults' | 'maxFiles' | 'timeout';
|
|
3
3
|
export declare const StoppedReasonSchema: z.ZodOptional<z.ZodEnum<{
|
|
4
4
|
maxResults: "maxResults";
|
|
5
|
-
timeout: "timeout";
|
|
6
5
|
maxFiles: "maxFiles";
|
|
6
|
+
timeout: "timeout";
|
|
7
7
|
}>>;
|
|
8
8
|
/**
|
|
9
9
|
* The subset `search_text` and `find_files` can actually emit. Their scans call
|
|
@@ -15,19 +15,6 @@ export declare const SearchStoppedReasonSchema: z.ZodOptional<z.ZodEnum<{
|
|
|
15
15
|
maxResults: "maxResults";
|
|
16
16
|
timeout: "timeout";
|
|
17
17
|
}>>;
|
|
18
|
-
/**
|
|
19
|
-
* Accumulates why an enumeration stopped early. maxResults wins over maxFiles
|
|
20
|
-
* wins over timeout (the most specific cap is the definite cause even if the
|
|
21
|
-
* abort also fired on the same iteration). Call `resolve()` once at the end.
|
|
22
|
-
*/
|
|
23
|
-
export declare class StopReasonTracker {
|
|
24
|
-
#private;
|
|
25
|
-
hitMaxResults(): void;
|
|
26
|
-
hitMaxFiles(): void;
|
|
27
|
-
hitAbort(): void;
|
|
28
|
-
get truncated(): boolean;
|
|
29
|
-
resolve(): StoppedReason | undefined;
|
|
30
|
-
}
|
|
31
18
|
interface ParallelResult<R> {
|
|
32
19
|
results: {
|
|
33
20
|
index: number;
|
|
@@ -46,8 +33,9 @@ export declare function processInParallel<T, R>(items: readonly T[], processor:
|
|
|
46
33
|
*
|
|
47
34
|
* `maxEntries` caps how many entries are dispatched (maxFiles); `shouldStop`
|
|
48
35
|
* lets the caller stop on its own accumulating result count (maxResults). Both
|
|
49
|
-
* are checked before dispatch, so the returned
|
|
50
|
-
*
|
|
36
|
+
* are checked before dispatch, so the returned reason is exactly one — the
|
|
37
|
+
* loop breaks on the first that fires. `undefined` means every entry was
|
|
38
|
+
* dispatched.
|
|
51
39
|
*/
|
|
52
40
|
export declare function processEntriesConcurrently(entries: AsyncIterable<{
|
|
53
41
|
path: string;
|
|
@@ -61,7 +49,7 @@ export declare function processEntriesConcurrently(entries: AsyncIterable<{
|
|
|
61
49
|
onEntry: () => void;
|
|
62
50
|
onError?: (entryPath: string, err: unknown) => void;
|
|
63
51
|
runEntry: (entryPath: string) => Promise<void>;
|
|
64
|
-
}): Promise<
|
|
52
|
+
}): Promise<StoppedReason | undefined>;
|
|
65
53
|
export declare function withAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T>;
|
|
66
54
|
export {};
|
|
67
55
|
//# sourceMappingURL=concurrency.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../../src/core/concurrency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAK5B,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;GAA2D,CAAC;AAE5F;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;GAA+C,CAAC;AAEtF
|
|
1
|
+
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../../src/core/concurrency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAK5B,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;GAA2D,CAAC;AAE5F;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;GAA+C,CAAC;AAEtF,UAAU,cAAc,CAAC,CAAC;IACxB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,EAAE,CAAC;IACvC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAC1C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,WAAW,GAAE,MAA6B,EAC1C,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAgE5B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACrE,OAAO,EAAE;IACP,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD,GACA,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CA0DpC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAmBlF"}
|