@j0hanz/filesystem-mcp 2.1.7 → 2.1.8

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 (99) hide show
  1. package/README.md +7 -7
  2. package/dist/cli.d.ts +1 -2
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +29 -34
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/concurrency.d.ts +4 -7
  7. package/dist/core/concurrency.d.ts.map +1 -1
  8. package/dist/core/concurrency.js +4 -12
  9. package/dist/core/concurrency.js.map +1 -1
  10. package/dist/core/cursor.d.ts.map +1 -1
  11. package/dist/core/cursor.js +8 -20
  12. package/dist/core/cursor.js.map +1 -1
  13. package/dist/core/fmt.d.ts +2 -1
  14. package/dist/core/fmt.d.ts.map +1 -1
  15. package/dist/core/fmt.js +8 -15
  16. package/dist/core/fmt.js.map +1 -1
  17. package/dist/core/fs.d.ts +0 -14
  18. package/dist/core/fs.d.ts.map +1 -1
  19. package/dist/core/fs.js +1 -0
  20. package/dist/core/fs.js.map +1 -1
  21. package/dist/core/glob.d.ts +0 -3
  22. package/dist/core/glob.d.ts.map +1 -1
  23. package/dist/core/glob.js +5 -27
  24. package/dist/core/glob.js.map +1 -1
  25. package/dist/core/input-required.d.ts +2 -2
  26. package/dist/core/input-required.d.ts.map +1 -1
  27. package/dist/core/input-required.js.map +1 -1
  28. package/dist/core/path-completer.d.ts.map +1 -1
  29. package/dist/core/path-completer.js +4 -27
  30. package/dist/core/path-completer.js.map +1 -1
  31. package/dist/core/path-utils.d.ts +1 -1
  32. package/dist/core/path-utils.d.ts.map +1 -1
  33. package/dist/core/path-utils.js +2 -2
  34. package/dist/core/path-utils.js.map +1 -1
  35. package/dist/core/path.d.ts +7 -9
  36. package/dist/core/path.d.ts.map +1 -1
  37. package/dist/core/path.js +24 -42
  38. package/dist/core/path.js.map +1 -1
  39. package/dist/core/read.d.ts.map +1 -1
  40. package/dist/core/read.js +6 -23
  41. package/dist/core/read.js.map +1 -1
  42. package/dist/core/schema.d.ts.map +1 -1
  43. package/dist/core/schema.js +19 -74
  44. package/dist/core/schema.js.map +1 -1
  45. package/dist/core/search.d.ts +1 -7
  46. package/dist/core/search.d.ts.map +1 -1
  47. package/dist/core/search.js +6 -13
  48. package/dist/core/search.js.map +1 -1
  49. package/dist/core/store.d.ts.map +1 -1
  50. package/dist/core/store.js +6 -17
  51. package/dist/core/store.js.map +1 -1
  52. package/dist/index.js +15 -42
  53. package/dist/index.js.map +1 -1
  54. package/dist/prompts.d.ts.map +1 -1
  55. package/dist/prompts.js +17 -25
  56. package/dist/prompts.js.map +1 -1
  57. package/dist/resources.d.ts +4 -4
  58. package/dist/resources.d.ts.map +1 -1
  59. package/dist/resources.js +7 -17
  60. package/dist/resources.js.map +1 -1
  61. package/dist/server.d.ts +1 -4
  62. package/dist/server.d.ts.map +1 -1
  63. package/dist/server.js +2 -7
  64. package/dist/server.js.map +1 -1
  65. package/dist/tools/define.d.ts.map +1 -1
  66. package/dist/tools/define.js +35 -61
  67. package/dist/tools/define.js.map +1 -1
  68. package/dist/tools/delete-file.d.ts.map +1 -1
  69. package/dist/tools/delete-file.js +30 -35
  70. package/dist/tools/delete-file.js.map +1 -1
  71. package/dist/tools/list.d.ts.map +1 -1
  72. package/dist/tools/list.js +3 -7
  73. package/dist/tools/list.js.map +1 -1
  74. package/dist/tools/move.d.ts.map +1 -1
  75. package/dist/tools/move.js +2 -4
  76. package/dist/tools/move.js.map +1 -1
  77. package/dist/tools/progress.d.ts +1 -1
  78. package/dist/tools/progress.d.ts.map +1 -1
  79. package/dist/tools/progress.js.map +1 -1
  80. package/dist/tools/read.d.ts.map +1 -1
  81. package/dist/tools/read.js +36 -42
  82. package/dist/tools/read.js.map +1 -1
  83. package/dist/tools/replace-in-files.d.ts.map +1 -1
  84. package/dist/tools/replace-in-files.js +4 -18
  85. package/dist/tools/replace-in-files.js.map +1 -1
  86. package/dist/tools/search-content.d.ts.map +1 -1
  87. package/dist/tools/search-content.js +3 -25
  88. package/dist/tools/search-content.js.map +1 -1
  89. package/dist/tools/stat.d.ts.map +1 -1
  90. package/dist/tools/stat.js +9 -37
  91. package/dist/tools/stat.js.map +1 -1
  92. package/dist/transport/http-policy.d.ts +18 -15
  93. package/dist/transport/http-policy.d.ts.map +1 -1
  94. package/dist/transport/http-policy.js +29 -39
  95. package/dist/transport/http-policy.js.map +1 -1
  96. package/dist/transport/http.d.ts.map +1 -1
  97. package/dist/transport/http.js +29 -39
  98. package/dist/transport/http.js.map +1 -1
  99. package/package.json +4 -7
package/README.md CHANGED
@@ -420,12 +420,12 @@ filesystem-mcp --port 3000
420
420
 
421
421
  ## Scripts
422
422
 
423
- | Mode | Command | Description |
424
- | :--------------- | :------------------------------- | :--------------------------------------------------- |
425
- | Full check | `node scripts/tasks.mjs` | Run build, type check, lint, format, knip, and tests |
426
- | Auto-fix + check | `node scripts/tasks.mjs fix` | Auto-fix formatting/linting and run the full check |
427
- | Static only | `node scripts/tasks.mjs --quick` | Run static analysis without tests |
428
- | Tests only | `node scripts/tasks.mjs test` | Run tests; accepts native `node --test` options |
423
+ | Mode | Command | Description |
424
+ | :--------------- | :--------------------- | :--------------------------------------------------- |
425
+ | Full check | `npm run check` | Run build, type check, lint, format, knip, and tests |
426
+ | Auto-fix + check | `npm run fix` | Auto-fix formatting/linting and run the full check |
427
+ | Static only | `npm run check:static` | Run static analysis without tests |
428
+ | Tests only | `npm test` | Run tests; accepts native `node --test` options |
429
429
 
430
430
  ## Security
431
431
 
@@ -444,7 +444,7 @@ filesystem-mcp --port 3000
444
444
  1. Fork the repository.
445
445
  2. Create a feature branch: `git checkout -b feat/your-feature`.
446
446
  3. Commit your changes with a clear message.
447
- 4. Run `node scripts/tasks.mjs` to confirm tests, types, lint, formatting, and knip all pass.
447
+ 4. Run `npm run check` to confirm tests, types, lint, formatting, and knip all pass.
448
448
  5. Open a pull request.
449
449
 
450
450
  [![Contributors](https://contrib.rocks/image?repo=j0hanz/filesystem-mcp)](https://github.com/j0hanz/filesystem-mcp/graphs/contributors)
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export declare class CliExitError extends Error {
2
- readonly exitCode: number;
3
- constructor(message: string, exitCode: number);
2
+ constructor(message: string);
4
3
  }
5
4
  export declare function parseArgs(): Promise<{
6
5
  allowedDirs: string[];
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAsBA,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAK9C;AAsGD,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,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,CAAC,CAgFD;AAMD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,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,CAqChB"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAsBA,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAqGD,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,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,CAAC,CA0ED;AAMD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,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,CAqChB"}
package/dist/cli.js CHANGED
@@ -13,23 +13,21 @@ import { registeredTools } from './tools/index.js';
13
13
  // Path & Config Utilities — pure functions and error types
14
14
  // ════════════════════════════════════════════════════════════
15
15
  export class CliExitError extends Error {
16
- exitCode;
17
- constructor(message, exitCode) {
16
+ constructor(message) {
18
17
  super(message);
19
18
  this.name = 'CliExitError';
20
- this.exitCode = exitCode;
21
19
  }
22
20
  }
23
21
  function validateCliPath(inputPath) {
24
22
  if (inputPath.includes('\0')) {
25
- throw new CliExitError('Path contains null bytes.', 1);
23
+ throw new CliExitError('Path contains null bytes.');
26
24
  }
27
25
  if (isWindowsDriveRelativePath(inputPath)) {
28
- throw new CliExitError('Windows drive-relative paths are not allowed. Use C:\\path or C:/path instead of C:path.', 1);
26
+ throw new CliExitError('Windows drive-relative paths are not allowed. Use C:\\path or C:/path instead of C:path.');
29
27
  }
30
28
  const reserved = getReservedDeviceNameForPath(inputPath);
31
29
  if (reserved) {
32
- throw new CliExitError(`Windows reserved device name not allowed: ${reserved}.`, 1);
30
+ throw new CliExitError(`Windows reserved device name not allowed: ${reserved}.`);
33
31
  }
34
32
  }
35
33
  async function validateDirectoryPath(inputPath, allowMissing = false) {
@@ -79,7 +77,7 @@ function parsePortOption(raw) {
79
77
  return undefined;
80
78
  const n = Number(raw);
81
79
  if (!Number.isInteger(n) || n < 1 || n > 65535) {
82
- throw new CliExitError(`Error: --port / FS_PORT must be an integer between 1 and 65535`, 1);
80
+ throw new CliExitError(`Error: --port / FS_PORT must be an integer between 1 and 65535`);
83
81
  }
84
82
  return n;
85
83
  }
@@ -118,42 +116,39 @@ export async function parseArgs() {
118
116
  for (const positional of parsed.positionals) {
119
117
  validateCliPath(positional);
120
118
  }
121
- const vals = parsed.values;
119
+ const v = parsed.values;
122
120
  // `--http-host` and `--api-key` are returned to the caller and handed to
123
121
  // the server as config. Every other flag lands in the CLI-override store
124
122
  // (core/config.ts) — the one owner of the flag-beats-env rule — which deep
125
123
  // core readers (path, sensitive, observability, util) consult before
126
124
  // falling back to the operator's environment. Nothing writes process.env.
127
- const httpHost = typeof vals['http-host'] === 'string' ? vals['http-host'] : process.env['FS_HTTP_HOST'];
128
- const apiKey = typeof vals['api-key'] === 'string' ? vals['api-key'] : process.env['FS_API_KEY'];
129
- if (typeof vals['log-level'] === 'string')
130
- cli.logLevel = vals['log-level'];
131
- if (typeof vals['max-file-size'] === 'string')
132
- cli.maxFileSize = vals['max-file-size'];
133
- if (typeof vals['root-boundary'] === 'string')
134
- cli.rootBoundary = vals['root-boundary'];
135
- if (vals['allow-sensitive'] === true)
125
+ const httpHost = v['http-host'] ?? process.env['FS_HTTP_HOST'];
126
+ const apiKey = v['api-key'] ?? process.env['FS_API_KEY'];
127
+ if (v['log-level'] !== undefined)
128
+ cli.logLevel = v['log-level'];
129
+ if (v['max-file-size'] !== undefined)
130
+ cli.maxFileSize = v['max-file-size'];
131
+ if (v['root-boundary'] !== undefined)
132
+ cli.rootBoundary = v['root-boundary'];
133
+ if (v['allow-sensitive'])
136
134
  cli.allowSensitive = true;
137
- if (vals['walk-cwd'] === true)
135
+ if (v['walk-cwd'])
138
136
  cli.allowCwdWalk = true;
139
- if (vals['allow-missing-roots'] === true)
137
+ if (v['allow-missing-roots'])
140
138
  cli.allowMissingRoots = true;
141
- if (Array.isArray(vals['deny'])) {
142
- const denyPatterns = vals['deny']
143
- .filter((entry) => typeof entry === 'string')
144
- .map((entry) => entry.trim())
145
- .filter(Boolean);
139
+ if (v.deny !== undefined) {
140
+ const denyPatterns = [...new Set(v.deny.map((entry) => entry.trim()).filter(Boolean))];
146
141
  if (denyPatterns.length > 0)
147
- cli.denyPatterns = [...new Set(denyPatterns)];
142
+ cli.denyPatterns = denyPatterns;
148
143
  }
149
- const allowCwd = vals['allow-cwd'] ||
150
- vals['walk-cwd'] ||
144
+ const allowCwd = v['allow-cwd'] ||
145
+ v['walk-cwd'] ||
151
146
  parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');
152
- const readOnly = vals['read-only'] || vals['safe'];
153
- const printConfig = vals['print-config'];
154
- const json = vals['json'];
155
- const port = parsePortOption(parsed.values.port ?? process.env['FS_PORT']);
156
- const allowMissingRoots = vals['allow-missing-roots'] ||
147
+ const readOnly = v['read-only'] || v.safe;
148
+ const printConfig = v['print-config'];
149
+ const json = v.json;
150
+ const port = parsePortOption(v.port ?? process.env['FS_PORT']);
151
+ const allowMissingRoots = v['allow-missing-roots'] ||
157
152
  parseTrueEnvFlag(process.env['FS_ALLOW_MISSING_ROOTS'], 'FS_ALLOW_MISSING_ROOTS');
158
153
  let allowedDirs = [];
159
154
  try {
@@ -163,7 +158,7 @@ export async function parseArgs() {
163
158
  : [];
164
159
  }
165
160
  catch (error) {
166
- throw new CliExitError(normalizeCliExitMessage(error), 1);
161
+ throw new CliExitError(normalizeCliExitMessage(error));
167
162
  }
168
163
  return {
169
164
  allowedDirs,
@@ -180,7 +175,7 @@ export async function parseArgs() {
180
175
  if (error instanceof CliExitError) {
181
176
  throw error;
182
177
  }
183
- throw new CliExitError(normalizeCliExitMessage(error), 1);
178
+ throw new CliExitError(normalizeCliExitMessage(error));
184
179
  }
185
180
  }
186
181
  // ════════════════════════════════════════════════════════════
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 readonly exitCode: number;\n\n constructor(message: string, exitCode: number) {\n super(message);\n this.name = 'CliExitError';\n this.exitCode = exitCode;\n }\n}\n\nfunction validateCliPath(inputPath: string): void {\n if (inputPath.includes('\\0')) {\n throw new CliExitError('Path contains null bytes.', 1);\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 1,\n );\n }\n\n const reserved = getReservedDeviceNameForPath(inputPath);\n if (reserved) {\n throw new CliExitError(`Windows reserved device name not allowed: ${reserved}.`, 1);\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`, 1);\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 vals = parsed.values as Record<string, unknown>;\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 =\n typeof vals['http-host'] === 'string' ? vals['http-host'] : process.env['FS_HTTP_HOST'];\n const apiKey =\n typeof vals['api-key'] === 'string' ? vals['api-key'] : process.env['FS_API_KEY'];\n\n if (typeof vals['log-level'] === 'string') cli.logLevel = vals['log-level'];\n if (typeof vals['max-file-size'] === 'string') cli.maxFileSize = vals['max-file-size'];\n if (typeof vals['root-boundary'] === 'string') cli.rootBoundary = vals['root-boundary'];\n if (vals['allow-sensitive'] === true) cli.allowSensitive = true;\n if (vals['walk-cwd'] === true) cli.allowCwdWalk = true;\n if (vals['allow-missing-roots'] === true) cli.allowMissingRoots = true;\n if (Array.isArray(vals['deny'])) {\n const denyPatterns = vals['deny']\n .filter((entry): entry is string => typeof entry === 'string')\n .map((entry) => entry.trim())\n .filter(Boolean);\n if (denyPatterns.length > 0) cli.denyPatterns = [...new Set(denyPatterns)];\n }\n\n const allowCwd =\n (vals['allow-cwd'] as boolean) ||\n (vals['walk-cwd'] as boolean) ||\n parseTrueEnvFlag(process.env['FS_ALLOW_CWD_WALK'], 'FS_ALLOW_CWD_WALK');\n const readOnly = (vals['read-only'] as boolean) || (vals['safe'] as boolean);\n const printConfig = vals['print-config'] as boolean;\n const json = vals['json'] as boolean;\n const port = parsePortOption(parsed.values.port ?? process.env['FS_PORT']);\n const allowMissingRoots =\n (vals['allow-missing-roots'] as boolean) ||\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), 1);\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), 1);\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.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"]}
@@ -28,7 +28,7 @@ export declare class StopReasonTracker {
28
28
  get truncated(): boolean;
29
29
  resolve(): StoppedReason | undefined;
30
30
  }
31
- export interface ParallelResult<R> {
31
+ interface ParallelResult<R> {
32
32
  results: {
33
33
  index: number;
34
34
  value: R;
@@ -51,6 +51,8 @@ export declare function processInParallel<T, R>(items: readonly T[], processor:
51
51
  */
52
52
  export declare function processEntriesConcurrently(entries: AsyncIterable<{
53
53
  path: string;
54
+ }> | Iterable<{
55
+ path: string;
54
56
  }>, options: {
55
57
  signal: AbortSignal | undefined;
56
58
  concurrency: number;
@@ -61,10 +63,5 @@ export declare function processEntriesConcurrently(entries: AsyncIterable<{
61
63
  runEntry: (entryPath: string) => Promise<void>;
62
64
  }): Promise<StopReasonTracker>;
63
65
  export declare function withAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T>;
64
- /**
65
- * `baseSignal` combined with a deadline. `AbortSignal.timeout`'s timer does not
66
- * hold the event loop open and is collected with the signal, so there is
67
- * nothing for callers to clean up.
68
- */
69
- export declare function timedSignal(baseSignal: AbortSignal | undefined, timeoutMs: number): AbortSignal;
66
+ export {};
70
67
  //# 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;;;;GAIG;AACH,qBAAa,iBAAiB;;IAG5B,aAAa,IAAI,IAAI;IAIrB,WAAW,IAAI,IAAI;IAInB,QAAQ,IAAI,IAAI;IAIhB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,IAAI,aAAa,GAAG,SAAS;CAGrC;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,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,CAiD5B;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACxC,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,iBAAiB,CAAC,CA0D5B;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAmBlF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,WAAW,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAG/F"}
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;;;;GAIG;AACH,qBAAa,iBAAiB;;IAG5B,aAAa,IAAI,IAAI;IAIrB,WAAW,IAAI,IAAI;IAInB,QAAQ,IAAI,IAAI;IAIhB,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,IAAI,aAAa,GAAG,SAAS;CAGrC;AAED,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,CAkD5B;AAED;;;;;;;;;GASG;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,iBAAiB,CAAC,CA0D5B;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAmBlF"}
@@ -69,9 +69,10 @@ export async function processInParallel(items, processor, concurrency = PARALLEL
69
69
  workers[index] = next();
70
70
  }
71
71
  await Promise.allSettled(workers);
72
- // A deadline (timedSignal) hit during the run surfaces here as signal.reason
73
- // — a TimeoutError — rather than a fresh AbortError, so callers see TIMEOUT,
74
- // not CANCELLED. The per-item throws above are swallowed by allSettled.
72
+ // A deadline (AbortSignal.timeout) hit during the run surfaces here as
73
+ // signal.reason — a TimeoutError — rather than a fresh AbortError, so callers
74
+ // see TIMEOUT, not CANCELLED. The per-item throws above are swallowed by
75
+ // allSettled.
75
76
  signal?.throwIfAborted();
76
77
  results.sort((left, right) => left.index - right.index);
77
78
  return { results, errors };
@@ -161,13 +162,4 @@ export function withAbort(promise, signal) {
161
162
  }
162
163
  });
163
164
  }
164
- /**
165
- * `baseSignal` combined with a deadline. `AbortSignal.timeout`'s timer does not
166
- * hold the event loop open and is collected with the signal, so there is
167
- * nothing for callers to clean up.
168
- */
169
- export function timedSignal(baseSignal, timeoutMs) {
170
- const deadline = AbortSignal.timeout(timeoutMs);
171
- return baseSignal ? AbortSignal.any([baseSignal, deadline]) : deadline;
172
- }
173
165
  //# sourceMappingURL=concurrency.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../src/core/concurrency.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { normalizeUnknownError } from './errors.js';\nimport { PARALLEL_CONCURRENCY } from './util.js';\n\nexport type StoppedReason = 'maxResults' | 'maxFiles' | 'timeout';\n\nexport const StoppedReasonSchema = z.enum(['maxResults', 'maxFiles', 'timeout']).optional();\n\n/**\n * The subset `search_text` and `find_files` can actually emit. Their scans call\n * only `hitMaxResults` and `hitAbort` (search.ts), never `hitMaxFiles`, so the\n * three-value enum published a value no response can carry and disagreed with\n * both tools' own descriptions.\n */\nexport const SearchStoppedReasonSchema = z.enum(['maxResults', 'timeout']).optional();\n\n/**\n * Accumulates why an enumeration stopped early. maxResults wins over maxFiles\n * wins over timeout (the most specific cap is the definite cause even if the\n * abort also fired on the same iteration). Call `resolve()` once at the end.\n */\nexport class StopReasonTracker {\n #reason?: StoppedReason;\n\n hitMaxResults(): void {\n this.#reason = 'maxResults';\n }\n\n hitMaxFiles(): void {\n if (this.#reason !== 'maxResults') this.#reason = 'maxFiles';\n }\n\n hitAbort(): void {\n this.#reason ??= 'timeout';\n }\n\n get truncated(): boolean {\n return this.#reason !== undefined;\n }\n\n resolve(): StoppedReason | undefined {\n return this.#reason;\n }\n}\n\nexport interface ParallelResult<R> {\n results: { index: number; value: R }[];\n errors: { index: number; error: Error }[];\n}\n\nexport async function processInParallel<T, R>(\n items: readonly T[],\n processor: (item: T) => Promise<R>,\n concurrency: number = PARALLEL_CONCURRENCY,\n signal?: AbortSignal,\n): Promise<ParallelResult<R>> {\n const itemCount = items.length;\n if (itemCount === 0) return { results: [], errors: [] };\n\n const results: { index: number; value: R }[] = [];\n const errors: { index: number; error: Error }[] = [];\n\n signal?.throwIfAborted();\n\n let nextIndex = 0;\n\n const next = async (): Promise<void> => {\n while (nextIndex < itemCount) {\n signal?.throwIfAborted();\n\n const index = nextIndex;\n nextIndex += 1;\n const item = items[index];\n if (item === undefined) {\n continue;\n }\n\n try {\n const value = await processor(item);\n signal?.throwIfAborted();\n results.push({ index, value });\n } catch (error) {\n errors.push({\n index,\n error: normalizeUnknownError(error),\n });\n }\n }\n };\n\n const workerCount = Math.min(itemCount, concurrency);\n const workers: Promise<void>[] = new Array<Promise<void>>(workerCount);\n for (let index = 0; index < workerCount; index += 1) {\n workers[index] = next();\n }\n\n await Promise.allSettled(workers);\n // A deadline (timedSignal) hit during the run surfaces here as signal.reason\n // — a TimeoutError — rather than a fresh AbortError, so callers see TIMEOUT,\n // not CANCELLED. The per-item throws above are swallowed by allSettled.\n signal?.throwIfAborted();\n\n results.sort((left, right) => left.index - right.index);\n return { results, errors };\n}\n\n/**\n * The streaming sibling of {@link processInParallel}: bounded-concurrency\n * dispatch over an AsyncIterable that may be far larger than the caller wants\n * to walk, so it stops early and reports why.\n *\n * `maxEntries` caps how many entries are dispatched (maxFiles); `shouldStop`\n * lets the caller stop on its own accumulating result count (maxResults). Both\n * are checked before dispatch, so the returned tracker names exactly one\n * reason — the loop breaks on the first that fires.\n */\nexport async function processEntriesConcurrently(\n entries: AsyncIterable<{ path: string }>,\n options: {\n signal: AbortSignal | undefined;\n concurrency: number;\n maxEntries?: number;\n shouldStop?: () => boolean;\n onEntry: () => void;\n onError?: (entryPath: string, err: unknown) => void;\n runEntry: (entryPath: string) => Promise<void>;\n },\n): Promise<StopReasonTracker> {\n const pending = new Set<Promise<void>>();\n const { signal, concurrency, maxEntries, shouldStop, onEntry, onError, runEntry } = options;\n const tracker = new StopReasonTracker();\n let dispatched = 0;\n\n const waitForSlot = async (): Promise<void> => {\n if (pending.size < concurrency) return;\n await Promise.race(pending);\n };\n\n for await (const entry of entries) {\n // The signal is cancellation OR the caller's timeout: stop dispatching and\n // let the caller report the run as incomplete rather than as a full sweep.\n if (signal?.aborted) {\n tracker.hitAbort();\n break;\n }\n if (maxEntries !== undefined && dispatched >= maxEntries) {\n tracker.hitMaxFiles();\n break;\n }\n // Check the result cap before waiting for a slot so an in-flight task that\n // already crossed the cap stops dispatch without an extra wait...\n if (shouldStop?.()) {\n tracker.hitMaxResults();\n break;\n }\n await waitForSlot();\n // ...and again after the slot frees, since tasks settle concurrently. The\n // cap can still be exceeded by at most `concurrency - 1` already-dispatched\n // tasks that are mid-flight; that overrun is inherent to concurrent dispatch.\n if (shouldStop?.()) {\n tracker.hitMaxResults();\n break;\n }\n onEntry();\n dispatched++;\n\n // Track a non-rejecting wrapper so a rejected task can never propagate out of\n // Promise.race(pending) in waitForSlot() and abort the loop before the final\n // drain below (which would silently abandon other in-flight tasks).\n // runEntry is expected to catch its own errors; if it unexpectedly throws,\n // record it as a failure rather than silently dropping it.\n const tracked = runEntry(entry.path).catch((err: unknown) => {\n onError?.(entry.path, err);\n });\n pending.add(tracked);\n void tracked.finally(() => {\n pending.delete(tracked);\n });\n }\n\n if (pending.size > 0) {\n await Promise.allSettled([...pending]);\n }\n\n return tracker;\n}\n\nexport function withAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) {\n return promise;\n }\n signal.throwIfAborted();\n let onAbort: (() => void) | undefined;\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n onAbort = () => {\n reject(signal.reason instanceof Error ? signal.reason : new Error('Operation aborted'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }),\n ]).finally(() => {\n if (onAbort) {\n signal.removeEventListener('abort', onAbort);\n }\n });\n}\n\n/**\n * `baseSignal` combined with a deadline. `AbortSignal.timeout`'s timer does not\n * hold the event loop open and is collected with the signal, so there is\n * nothing for callers to clean up.\n */\nexport function timedSignal(baseSignal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\n const deadline = AbortSignal.timeout(timeoutMs);\n return baseSignal ? AbortSignal.any([baseSignal, deadline]) : deadline;\n}\n"]}
1
+ {"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../src/core/concurrency.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { normalizeUnknownError } from './errors.js';\nimport { PARALLEL_CONCURRENCY } from './util.js';\n\nexport type StoppedReason = 'maxResults' | 'maxFiles' | 'timeout';\n\nexport const StoppedReasonSchema = z.enum(['maxResults', 'maxFiles', 'timeout']).optional();\n\n/**\n * The subset `search_text` and `find_files` can actually emit. Their scans call\n * only `hitMaxResults` and `hitAbort` (search.ts), never `hitMaxFiles`, so the\n * three-value enum published a value no response can carry and disagreed with\n * both tools' own descriptions.\n */\nexport const SearchStoppedReasonSchema = z.enum(['maxResults', 'timeout']).optional();\n\n/**\n * Accumulates why an enumeration stopped early. maxResults wins over maxFiles\n * wins over timeout (the most specific cap is the definite cause even if the\n * abort also fired on the same iteration). Call `resolve()` once at the end.\n */\nexport class StopReasonTracker {\n #reason?: StoppedReason;\n\n hitMaxResults(): void {\n this.#reason = 'maxResults';\n }\n\n hitMaxFiles(): void {\n if (this.#reason !== 'maxResults') this.#reason = 'maxFiles';\n }\n\n hitAbort(): void {\n this.#reason ??= 'timeout';\n }\n\n get truncated(): boolean {\n return this.#reason !== undefined;\n }\n\n resolve(): StoppedReason | undefined {\n return this.#reason;\n }\n}\n\ninterface ParallelResult<R> {\n results: { index: number; value: R }[];\n errors: { index: number; error: Error }[];\n}\n\nexport async function processInParallel<T, R>(\n items: readonly T[],\n processor: (item: T) => Promise<R>,\n concurrency: number = PARALLEL_CONCURRENCY,\n signal?: AbortSignal,\n): Promise<ParallelResult<R>> {\n const itemCount = items.length;\n if (itemCount === 0) return { results: [], errors: [] };\n\n const results: { index: number; value: R }[] = [];\n const errors: { index: number; error: Error }[] = [];\n\n signal?.throwIfAborted();\n\n let nextIndex = 0;\n\n const next = async (): Promise<void> => {\n while (nextIndex < itemCount) {\n signal?.throwIfAborted();\n\n const index = nextIndex;\n nextIndex += 1;\n const item = items[index];\n if (item === undefined) {\n continue;\n }\n\n try {\n const value = await processor(item);\n signal?.throwIfAborted();\n results.push({ index, value });\n } catch (error) {\n errors.push({\n index,\n error: normalizeUnknownError(error),\n });\n }\n }\n };\n\n const workerCount = Math.min(itemCount, concurrency);\n const workers: Promise<void>[] = new Array<Promise<void>>(workerCount);\n for (let index = 0; index < workerCount; index += 1) {\n workers[index] = next();\n }\n\n await Promise.allSettled(workers);\n // A deadline (AbortSignal.timeout) hit during the run surfaces here as\n // signal.reason — a TimeoutError — rather than a fresh AbortError, so callers\n // see TIMEOUT, not CANCELLED. The per-item throws above are swallowed by\n // allSettled.\n signal?.throwIfAborted();\n\n results.sort((left, right) => left.index - right.index);\n return { results, errors };\n}\n\n/**\n * The streaming sibling of {@link processInParallel}: bounded-concurrency\n * dispatch over an AsyncIterable that may be far larger than the caller wants\n * to walk, so it stops early and reports why.\n *\n * `maxEntries` caps how many entries are dispatched (maxFiles); `shouldStop`\n * lets the caller stop on its own accumulating result count (maxResults). Both\n * are checked before dispatch, so the returned tracker names exactly one\n * reason — the loop breaks on the first that fires.\n */\nexport async function processEntriesConcurrently(\n entries: AsyncIterable<{ path: string }> | Iterable<{ path: string }>,\n options: {\n signal: AbortSignal | undefined;\n concurrency: number;\n maxEntries?: number;\n shouldStop?: () => boolean;\n onEntry: () => void;\n onError?: (entryPath: string, err: unknown) => void;\n runEntry: (entryPath: string) => Promise<void>;\n },\n): Promise<StopReasonTracker> {\n const pending = new Set<Promise<void>>();\n const { signal, concurrency, maxEntries, shouldStop, onEntry, onError, runEntry } = options;\n const tracker = new StopReasonTracker();\n let dispatched = 0;\n\n const waitForSlot = async (): Promise<void> => {\n if (pending.size < concurrency) return;\n await Promise.race(pending);\n };\n\n for await (const entry of entries) {\n // The signal is cancellation OR the caller's timeout: stop dispatching and\n // let the caller report the run as incomplete rather than as a full sweep.\n if (signal?.aborted) {\n tracker.hitAbort();\n break;\n }\n if (maxEntries !== undefined && dispatched >= maxEntries) {\n tracker.hitMaxFiles();\n break;\n }\n // Check the result cap before waiting for a slot so an in-flight task that\n // already crossed the cap stops dispatch without an extra wait...\n if (shouldStop?.()) {\n tracker.hitMaxResults();\n break;\n }\n await waitForSlot();\n // ...and again after the slot frees, since tasks settle concurrently. The\n // cap can still be exceeded by at most `concurrency - 1` already-dispatched\n // tasks that are mid-flight; that overrun is inherent to concurrent dispatch.\n if (shouldStop?.()) {\n tracker.hitMaxResults();\n break;\n }\n onEntry();\n dispatched++;\n\n // Track a non-rejecting wrapper so a rejected task can never propagate out of\n // Promise.race(pending) in waitForSlot() and abort the loop before the final\n // drain below (which would silently abandon other in-flight tasks).\n // runEntry is expected to catch its own errors; if it unexpectedly throws,\n // record it as a failure rather than silently dropping it.\n const tracked = runEntry(entry.path).catch((err: unknown) => {\n onError?.(entry.path, err);\n });\n pending.add(tracked);\n void tracked.finally(() => {\n pending.delete(tracked);\n });\n }\n\n if (pending.size > 0) {\n await Promise.allSettled([...pending]);\n }\n\n return tracker;\n}\n\nexport function withAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) {\n return promise;\n }\n signal.throwIfAborted();\n let onAbort: (() => void) | undefined;\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n onAbort = () => {\n reject(signal.reason instanceof Error ? signal.reason : new Error('Operation aborted'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }),\n ]).finally(() => {\n if (onAbort) {\n signal.removeEventListener('abort', onAbort);\n }\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgCvE;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,IAAI,CAAC,CAAC,EAAE,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAE,SAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;IAC9C,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;CACrE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CA6BlC"}
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgCvE;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,IAAI,CAAC,CAAC,EAAE,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAE,SAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;IAC9C,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;CACrE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAwBlC"}
@@ -36,27 +36,15 @@ export async function paginate(params) {
36
36
  return pageResult(decoded.snapshotId, decoded.offset, params.pageSize, snapshot);
37
37
  }
38
38
  const produced = await params.produce();
39
- const incomplete = produced.items.length > params.pageSize || produced.truncated;
40
- let first;
41
- if (produced.items.length <= params.pageSize) {
42
- first = {
43
- page: produced.items,
44
- metadata: produced.metadata,
45
- nextCursor: undefined,
46
- offset: 0,
47
- };
48
- }
49
- else {
50
- const snapshotId = params.store.create({
39
+ const { items, metadata } = produced;
40
+ const incomplete = items.length > params.pageSize || produced.truncated;
41
+ const first = items.length <= params.pageSize
42
+ ? { page: items, metadata, nextCursor: undefined, offset: 0 }
43
+ : pageResult(params.store.create({
51
44
  queryKey: params.queryKey,
52
- items: produced.items,
53
- metadata: produced.metadata,
54
- });
55
- first = pageResult(snapshotId, 0, params.pageSize, {
56
- items: produced.items,
57
- metadata: produced.metadata,
58
- });
59
- }
45
+ items,
46
+ metadata,
47
+ }), 0, params.pageSize, { items, metadata });
60
48
  if (!incomplete || params.externalize === undefined)
61
49
  return first;
62
50
  return { ...first, resource: params.externalize(produced.items, produced.metadata) };
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ErrorCode, FsError } from './errors.js';\nimport type { PageSnapshot, PageSnapshotStore } from './page-store.js';\nimport { invalidCursor } from './page-store.js';\n\ninterface PageCursor {\n readonly snapshotId: string;\n readonly offset: number;\n}\n\nfunction encodePageCursor(cursor: PageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction pageResult<T, M>(\n snapshotId: string,\n offset: number,\n pageSize: number,\n snapshot: PageSnapshot<T, M>,\n): Page<T, M> {\n if (offset >= snapshot.items.length) throw invalidCursor();\n const page = snapshot.items.slice(offset, offset + pageSize);\n const nextOffset = offset + page.length;\n return {\n page,\n metadata: snapshot.metadata,\n offset,\n nextCursor:\n nextOffset < snapshot.items.length\n ? encodePageCursor({ snapshotId, offset: nextOffset })\n : undefined,\n };\n}\n\n/**\n * The full result set a paged tool produced, before slicing. `truncated` is the\n * engine's own stop state — the hard result cap or a timeout cut the set — and\n * says nothing about page size.\n */\nexport interface ProducedPage<T, M> {\n readonly items: readonly T[];\n readonly metadata: M;\n readonly truncated: boolean;\n}\n\ninterface Page<T, M> {\n readonly page: readonly T[];\n readonly metadata: M;\n readonly nextCursor: string | undefined;\n /**\n * Index of this page's first item in the full set. A tool that reports\n * progress in its text needs it: without it every page can only say how many\n * rows it holds, which is the same number on page 1 and page 9.\n */\n readonly offset: number;\n}\n\nexport interface PaginatedPage<T, M, R> extends Page<T, M> {\n /** The externalized full set. First page only, and only when incomplete. */\n readonly resource?: R;\n}\n\n/**\n * The one replay-or-produce branch every paged tool runs. A cursor replays a\n * stored snapshot; without one, `produce` runs the query and its result is\n * snapshotted for later pages.\n *\n * The response is incomplete when the set spans more than one page or the\n * engine truncated it. Then — and only on the first page — `externalize`\n * stores the full set and its handle rides back as `resource`. Replayed pages\n * never carry it: the stored entry expires on the resource store's own TTL and\n * LRU, not this snapshot's, so replaying its URI would hand back a dead\n * pointer. Nor do they mint a new one; the set was already stored whole.\n */\nexport async function paginate<T, M, R>(params: {\n store: PageSnapshotStore;\n queryKey: string;\n cursor: string | undefined;\n pageSize: number;\n produce: () => Promise<ProducedPage<T, M>>;\n externalize?: ((items: readonly T[], metadata: M) => R) | undefined;\n}): Promise<PaginatedPage<T, M, R>> {\n if (params.cursor !== undefined) {\n const decoded = decodePageCursor(params.cursor);\n const snapshot = params.store.read<T, M>(decoded.snapshotId, params.queryKey);\n return pageResult(decoded.snapshotId, decoded.offset, params.pageSize, snapshot);\n }\n const produced = await params.produce();\n const incomplete = produced.items.length > params.pageSize || produced.truncated;\n let first: Page<T, M>;\n if (produced.items.length <= params.pageSize) {\n first = {\n page: produced.items,\n metadata: produced.metadata,\n nextCursor: undefined,\n offset: 0,\n };\n } else {\n const snapshotId = params.store.create({\n queryKey: params.queryKey,\n items: produced.items,\n metadata: produced.metadata,\n });\n first = pageResult(snapshotId, 0, params.pageSize, {\n items: produced.items,\n metadata: produced.metadata,\n });\n }\n if (!incomplete || params.externalize === undefined) return first;\n return { ...first, resource: params.externalize(produced.items, produced.metadata) };\n}\n\nfunction decodePageCursor(cursor: string): PageCursor {\n try {\n const text = Buffer.from(cursor, 'base64url').toString('utf-8');\n const parsed = JSON.parse(text) as { snapshotId?: unknown; offset?: unknown };\n if (\n typeof parsed.snapshotId === 'string' &&\n parsed.snapshotId.length > 0 &&\n typeof parsed.offset === 'number' &&\n Number.isInteger(parsed.offset) &&\n parsed.offset >= 0\n ) {\n return { snapshotId: parsed.snapshotId, offset: parsed.offset };\n }\n throw new Error('Invalid page cursor');\n } catch (error) {\n if (error instanceof FsError) throw error;\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n invalidCursor().message,\n undefined,\n error instanceof Error ? error : undefined,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/core/cursor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ErrorCode, FsError } from './errors.js';\nimport type { PageSnapshot, PageSnapshotStore } from './page-store.js';\nimport { invalidCursor } from './page-store.js';\n\ninterface PageCursor {\n readonly snapshotId: string;\n readonly offset: number;\n}\n\nfunction encodePageCursor(cursor: PageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction pageResult<T, M>(\n snapshotId: string,\n offset: number,\n pageSize: number,\n snapshot: PageSnapshot<T, M>,\n): Page<T, M> {\n if (offset >= snapshot.items.length) throw invalidCursor();\n const page = snapshot.items.slice(offset, offset + pageSize);\n const nextOffset = offset + page.length;\n return {\n page,\n metadata: snapshot.metadata,\n offset,\n nextCursor:\n nextOffset < snapshot.items.length\n ? encodePageCursor({ snapshotId, offset: nextOffset })\n : undefined,\n };\n}\n\n/**\n * The full result set a paged tool produced, before slicing. `truncated` is the\n * engine's own stop state — the hard result cap or a timeout cut the set — and\n * says nothing about page size.\n */\nexport interface ProducedPage<T, M> {\n readonly items: readonly T[];\n readonly metadata: M;\n readonly truncated: boolean;\n}\n\ninterface Page<T, M> {\n readonly page: readonly T[];\n readonly metadata: M;\n readonly nextCursor: string | undefined;\n /**\n * Index of this page's first item in the full set. A tool that reports\n * progress in its text needs it: without it every page can only say how many\n * rows it holds, which is the same number on page 1 and page 9.\n */\n readonly offset: number;\n}\n\nexport interface PaginatedPage<T, M, R> extends Page<T, M> {\n /** The externalized full set. First page only, and only when incomplete. */\n readonly resource?: R;\n}\n\n/**\n * The one replay-or-produce branch every paged tool runs. A cursor replays a\n * stored snapshot; without one, `produce` runs the query and its result is\n * snapshotted for later pages.\n *\n * The response is incomplete when the set spans more than one page or the\n * engine truncated it. Then — and only on the first page — `externalize`\n * stores the full set and its handle rides back as `resource`. Replayed pages\n * never carry it: the stored entry expires on the resource store's own TTL and\n * LRU, not this snapshot's, so replaying its URI would hand back a dead\n * pointer. Nor do they mint a new one; the set was already stored whole.\n */\nexport async function paginate<T, M, R>(params: {\n store: PageSnapshotStore;\n queryKey: string;\n cursor: string | undefined;\n pageSize: number;\n produce: () => Promise<ProducedPage<T, M>>;\n externalize?: ((items: readonly T[], metadata: M) => R) | undefined;\n}): Promise<PaginatedPage<T, M, R>> {\n if (params.cursor !== undefined) {\n const decoded = decodePageCursor(params.cursor);\n const snapshot = params.store.read<T, M>(decoded.snapshotId, params.queryKey);\n return pageResult(decoded.snapshotId, decoded.offset, params.pageSize, snapshot);\n }\n const produced = await params.produce();\n const { items, metadata } = produced;\n const incomplete = items.length > params.pageSize || produced.truncated;\n const first: Page<T, M> =\n items.length <= params.pageSize\n ? { page: items, metadata, nextCursor: undefined, offset: 0 }\n : pageResult(\n params.store.create({\n queryKey: params.queryKey,\n items,\n metadata,\n }),\n 0,\n params.pageSize,\n { items, metadata },\n );\n if (!incomplete || params.externalize === undefined) return first;\n return { ...first, resource: params.externalize(produced.items, produced.metadata) };\n}\n\nfunction decodePageCursor(cursor: string): PageCursor {\n try {\n const text = Buffer.from(cursor, 'base64url').toString('utf-8');\n const parsed = JSON.parse(text) as { snapshotId?: unknown; offset?: unknown };\n if (\n typeof parsed.snapshotId === 'string' &&\n parsed.snapshotId.length > 0 &&\n typeof parsed.offset === 'number' &&\n Number.isInteger(parsed.offset) &&\n parsed.offset >= 0\n ) {\n return { snapshotId: parsed.snapshotId, offset: parsed.offset };\n }\n throw new Error('Invalid page cursor');\n } catch (error) {\n if (error instanceof FsError) throw error;\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n invalidCursor().message,\n undefined,\n error instanceof Error ? error : undefined,\n );\n }\n}\n"]}
@@ -7,7 +7,7 @@ export interface ProgressCtx {
7
7
  detail?: string;
8
8
  error?: string;
9
9
  }
10
- export type Phase = 'tick' | 'done' | 'fail';
10
+ type Phase = 'tick' | 'done' | 'fail';
11
11
  export declare function plainMessage(phase: Phase, ctx: ProgressCtx): string;
12
12
  export declare function formatCount(count: number, singular: string, plural?: string): string;
13
13
  /**
@@ -52,4 +52,5 @@ export declare const cliFmt: {
52
52
  section: (t: string) => string;
53
53
  bool: (v: boolean) => string;
54
54
  };
55
+ export {};
55
56
  //# sourceMappingURL=fmt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fmt.d.ts","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAkC7C,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAGnE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAiB,GAAG,MAAM,CAE5F;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAKD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,SAAQ,GAAG,MAAM,CAI9E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAE/E;AAYD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,GAAG,MAAM,CAoBT;AAUD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9D;AASD,eAAO,MAAM,MAAM;cACP,MAAM;aACP,MAAM;cACL,MAAM;gBACJ,MAAM;cACR,MAAM;qBACC,MAAM;iBACV,MAAM;cACT,OAAO;CAClB,CAAC"}
1
+ {"version":3,"file":"fmt.d.ts","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAkCtC,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAGnE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAiB,GAAG,MAAM,CAE5F;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAKD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,SAAQ,GAAG,MAAM,CAI9E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAE/E;AAYD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,GAAG,MAAM,CAoBT;AAMD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,eAAO,MAAM,MAAM;cACP,MAAM;aACP,MAAM;cACL,MAAM;gBACJ,MAAM;cACR,MAAM;qBACC,MAAM;iBACV,MAAM;cACT,OAAO;CAClB,CAAC"}
package/dist/core/fmt.js CHANGED
@@ -99,25 +99,18 @@ export function pageTrailer(p) {
99
99
  // ---------------------------------------------------------------------------
100
100
  // CLI color helpers
101
101
  // ---------------------------------------------------------------------------
102
- function isColorEnabled() {
103
- return process.stdout.isTTY && !process.env['NO_COLOR'];
104
- }
105
102
  export function padEndVisible(s, width) {
106
103
  const visible = stripVTControlCharacters(s).length;
107
104
  return visible >= width ? s : s + ' '.repeat(width - visible);
108
105
  }
109
- /** Same palette as above, but only when stdout wants color. */
110
- function tint(format, text) {
111
- return isColorEnabled() ? styleText(format, text) : text;
112
- }
113
106
  export const cliFmt = {
114
- bold: (t) => tint('bold', t),
115
- dim: (t) => tint('dim', t),
116
- cyan: (t) => tint('cyan', t),
117
- yellow: (t) => tint('yellow', t),
118
- flag: (t) => tint('green', t),
119
- placeholder: (t) => tint('yellow', t),
120
- section: (t) => tint(['cyan', 'bold'], t),
121
- bool: (v) => (v ? tint('green', 'true') : tint('red', 'false')),
107
+ bold: (t) => styleText('bold', t),
108
+ dim: (t) => styleText('dim', t),
109
+ cyan: (t) => styleText('cyan', t),
110
+ yellow: (t) => styleText('yellow', t),
111
+ flag: (t) => styleText('green', t),
112
+ placeholder: (t) => styleText('yellow', t),
113
+ section: (t) => styleText(['cyan', 'bold'], t),
114
+ bool: (v) => (v ? styleText('green', 'true') : styleText('red', 'false')),
122
115
  };
123
116
  //# sourceMappingURL=fmt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fmt.js","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename } from 'node:path';\nimport { stripVTControlCharacters, styleText } from 'node:util';\n\nimport { MAX_SEARCH_RESULTS } from './util.js';\n\nexport interface ProgressCtx {\n label: string;\n subject?: string;\n scope?: string;\n current?: number;\n total?: number;\n detail?: string;\n error?: string;\n}\n\nexport type Phase = 'tick' | 'done' | 'fail';\n\nfunction buildBody(ctx: ProgressCtx, phase: Phase): string {\n const items: string[] = [];\n if (ctx.subject) {\n items.push(ctx.subject);\n }\n\n switch (phase) {\n case 'tick':\n if (ctx.current !== undefined && ctx.total !== undefined) {\n items.push(`${ctx.current}/${ctx.total}`);\n } else if (ctx.current !== undefined) {\n items.push(String(ctx.current));\n }\n break;\n case 'done':\n if (ctx.scope) {\n items.push(ctx.scope);\n }\n if (ctx.detail) {\n items.push(ctx.detail);\n }\n break;\n case 'fail':\n if (ctx.error) {\n items.push(ctx.error);\n }\n break;\n }\n\n return items.join(' · ');\n}\n\nexport function plainMessage(phase: Phase, ctx: ProgressCtx): string {\n const body = buildBody(ctx, phase);\n return body ? `${ctx.label}: ${body}` : `${ctx.label}:`;\n}\n\nexport function formatCount(count: number, singular: string, plural = `${singular}s`): string {\n return `${String(count)} ${count === 1 ? singular : plural}`;\n}\n\n/**\n * Short label for a path in summary text. `basename` returns '' for a root\n * (`C:\\`, `/`) and for a trailing-slash path, which rendered as a bare\n * separator in the per-path summaries, so fall back to the full path.\n */\nexport function pathLabel(path: string): string {\n return basename(path) || path;\n}\n\n/** Names past this point are noise in a summary line; structuredContent holds them all. */\nconst MAX_ROSTER_ITEMS = 20;\n\n/**\n * Join a per-path summary roster, capped. A 1000-path delete would otherwise\n * name every entry in the text block that `structuredContent` already carries\n * in full.\n */\nexport function joinRoster(items: readonly string[], separator = ' · '): string {\n if (items.length <= MAX_ROSTER_ITEMS) return items.join(separator);\n const shown = items.slice(0, MAX_ROSTER_ITEMS).join(separator);\n return `${shown}${separator}+${String(items.length - MAX_ROSTER_ITEMS)} more`;\n}\n\nexport function truncateProgressPattern(pattern: string, maxLength = 40): string {\n return pattern.length <= maxLength ? pattern : `${pattern.slice(0, maxLength)}…`;\n}\n\n/**\n * `stoppedReason` names the engine's own cap, and `maxResults` is also the name\n * of the caller's page-size argument — spelling the cap out keeps a caller who\n * passed `maxResults: 5` from reading their own argument back.\n */\nconst STOP_REASON_TEXT: Record<string, string> = {\n maxResults: `hit the server's ${String(MAX_SEARCH_RESULTS)}-result scan cap, not your maxResults`,\n timeout: 'hit the time limit, or the request was cancelled',\n};\n\n/**\n * The `//` lines a paged search appends to its text block. Paging and the\n * engine's stop state live in the structured half, which `defineTool` ships\n * under `_meta` for a tool that authors its own text — and no client renders\n * that. These lines are the only place a caller learns that more remains or\n * that the scan was cut. They are independent: a scan can stop early with too\n * few results to page, so a truncation with no cursor must still say so.\n * Prefixed `//` so neither can be mistaken for a result row.\n */\nexport function pageTrailer(p: {\n offset: number;\n shown: number;\n total: number;\n noun: string;\n tool: string;\n nextCursor?: string | undefined;\n stoppedReason?: string | undefined;\n}): string {\n const lines: string[] = [];\n // Position is owed on every page of a split set, including the last one —\n // which has no cursor and would otherwise read as the whole answer.\n if (p.offset > 0 || p.total > p.shown) {\n const next =\n p.nextCursor === undefined\n ? ''\n : ` Next page: ${p.tool} ${JSON.stringify({ cursor: p.nextCursor })}`;\n lines.push(\n `// showing ${String(p.offset + 1)}-${String(p.offset + p.shown)} of ${String(p.total)} ${p.noun}.${next}`,\n );\n }\n if (p.stoppedReason !== undefined) {\n const cause = STOP_REASON_TEXT[p.stoppedReason] ?? `stopped (${p.stoppedReason})`;\n lines.push(\n `// scan stopped early: ${cause}. That total is a floor, not the count. Narrow path or pattern for the rest.`,\n );\n }\n return lines.length > 0 ? `\\n\\n${lines.join('\\n')}` : '';\n}\n\n// ---------------------------------------------------------------------------\n// CLI color helpers\n// ---------------------------------------------------------------------------\n\nfunction isColorEnabled(): boolean {\n return process.stdout.isTTY && !process.env['NO_COLOR'];\n}\n\nexport function padEndVisible(s: string, width: number): string {\n const visible = stripVTControlCharacters(s).length;\n return visible >= width ? s : s + ' '.repeat(width - visible);\n}\n\ntype Style = Parameters<typeof styleText>[0];\n\n/** Same palette as above, but only when stdout wants color. */\nfunction tint(format: Style, text: string): string {\n return isColorEnabled() ? styleText(format, text) : text;\n}\n\nexport const cliFmt = {\n bold: (t: string) => tint('bold', t),\n dim: (t: string) => tint('dim', t),\n cyan: (t: string) => tint('cyan', t),\n yellow: (t: string) => tint('yellow', t),\n flag: (t: string) => tint('green', t),\n placeholder: (t: string) => tint('yellow', t),\n section: (t: string) => tint(['cyan', 'bold'], t),\n bool: (v: boolean) => (v ? tint('green', 'true') : tint('red', 'false')),\n};\n"]}
1
+ {"version":3,"file":"fmt.js","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename } from 'node:path';\nimport { stripVTControlCharacters, styleText } from 'node:util';\n\nimport { MAX_SEARCH_RESULTS } from './util.js';\n\nexport interface ProgressCtx {\n label: string;\n subject?: string;\n scope?: string;\n current?: number;\n total?: number;\n detail?: string;\n error?: string;\n}\n\ntype Phase = 'tick' | 'done' | 'fail';\n\nfunction buildBody(ctx: ProgressCtx, phase: Phase): string {\n const items: string[] = [];\n if (ctx.subject) {\n items.push(ctx.subject);\n }\n\n switch (phase) {\n case 'tick':\n if (ctx.current !== undefined && ctx.total !== undefined) {\n items.push(`${ctx.current}/${ctx.total}`);\n } else if (ctx.current !== undefined) {\n items.push(String(ctx.current));\n }\n break;\n case 'done':\n if (ctx.scope) {\n items.push(ctx.scope);\n }\n if (ctx.detail) {\n items.push(ctx.detail);\n }\n break;\n case 'fail':\n if (ctx.error) {\n items.push(ctx.error);\n }\n break;\n }\n\n return items.join(' · ');\n}\n\nexport function plainMessage(phase: Phase, ctx: ProgressCtx): string {\n const body = buildBody(ctx, phase);\n return body ? `${ctx.label}: ${body}` : `${ctx.label}:`;\n}\n\nexport function formatCount(count: number, singular: string, plural = `${singular}s`): string {\n return `${String(count)} ${count === 1 ? singular : plural}`;\n}\n\n/**\n * Short label for a path in summary text. `basename` returns '' for a root\n * (`C:\\`, `/`) and for a trailing-slash path, which rendered as a bare\n * separator in the per-path summaries, so fall back to the full path.\n */\nexport function pathLabel(path: string): string {\n return basename(path) || path;\n}\n\n/** Names past this point are noise in a summary line; structuredContent holds them all. */\nconst MAX_ROSTER_ITEMS = 20;\n\n/**\n * Join a per-path summary roster, capped. A 1000-path delete would otherwise\n * name every entry in the text block that `structuredContent` already carries\n * in full.\n */\nexport function joinRoster(items: readonly string[], separator = ' · '): string {\n if (items.length <= MAX_ROSTER_ITEMS) return items.join(separator);\n const shown = items.slice(0, MAX_ROSTER_ITEMS).join(separator);\n return `${shown}${separator}+${String(items.length - MAX_ROSTER_ITEMS)} more`;\n}\n\nexport function truncateProgressPattern(pattern: string, maxLength = 40): string {\n return pattern.length <= maxLength ? pattern : `${pattern.slice(0, maxLength)}…`;\n}\n\n/**\n * `stoppedReason` names the engine's own cap, and `maxResults` is also the name\n * of the caller's page-size argument — spelling the cap out keeps a caller who\n * passed `maxResults: 5` from reading their own argument back.\n */\nconst STOP_REASON_TEXT: Record<string, string> = {\n maxResults: `hit the server's ${String(MAX_SEARCH_RESULTS)}-result scan cap, not your maxResults`,\n timeout: 'hit the time limit, or the request was cancelled',\n};\n\n/**\n * The `//` lines a paged search appends to its text block. Paging and the\n * engine's stop state live in the structured half, which `defineTool` ships\n * under `_meta` for a tool that authors its own text — and no client renders\n * that. These lines are the only place a caller learns that more remains or\n * that the scan was cut. They are independent: a scan can stop early with too\n * few results to page, so a truncation with no cursor must still say so.\n * Prefixed `//` so neither can be mistaken for a result row.\n */\nexport function pageTrailer(p: {\n offset: number;\n shown: number;\n total: number;\n noun: string;\n tool: string;\n nextCursor?: string | undefined;\n stoppedReason?: string | undefined;\n}): string {\n const lines: string[] = [];\n // Position is owed on every page of a split set, including the last one —\n // which has no cursor and would otherwise read as the whole answer.\n if (p.offset > 0 || p.total > p.shown) {\n const next =\n p.nextCursor === undefined\n ? ''\n : ` Next page: ${p.tool} ${JSON.stringify({ cursor: p.nextCursor })}`;\n lines.push(\n `// showing ${String(p.offset + 1)}-${String(p.offset + p.shown)} of ${String(p.total)} ${p.noun}.${next}`,\n );\n }\n if (p.stoppedReason !== undefined) {\n const cause = STOP_REASON_TEXT[p.stoppedReason] ?? `stopped (${p.stoppedReason})`;\n lines.push(\n `// scan stopped early: ${cause}. That total is a floor, not the count. Narrow path or pattern for the rest.`,\n );\n }\n return lines.length > 0 ? `\\n\\n${lines.join('\\n')}` : '';\n}\n\n// ---------------------------------------------------------------------------\n// CLI color helpers\n// ---------------------------------------------------------------------------\n\nexport function padEndVisible(s: string, width: number): string {\n const visible = stripVTControlCharacters(s).length;\n return visible >= width ? s : s + ' '.repeat(width - visible);\n}\n\nexport const cliFmt = {\n bold: (t: string) => styleText('bold', t),\n dim: (t: string) => styleText('dim', t),\n cyan: (t: string) => styleText('cyan', t),\n yellow: (t: string) => styleText('yellow', t),\n flag: (t: string) => styleText('green', t),\n placeholder: (t: string) => styleText('yellow', t),\n section: (t: string) => styleText(['cyan', 'bold'], t),\n bool: (v: boolean) => (v ? styleText('green', 'true') : styleText('red', 'false')),\n};\n"]}
package/dist/core/fs.d.ts CHANGED
@@ -7,20 +7,6 @@ import type { ReadFileResult, ReadSpec } from './read.js';
7
7
  export type { FileType };
8
8
  export type { Stats };
9
9
  export type { FileHandle };
10
- export interface FileInfo {
11
- readonly name: string;
12
- readonly path: string;
13
- readonly type: FileType;
14
- readonly size: number;
15
- readonly tokenEstimate?: number;
16
- readonly created: Date;
17
- readonly modified: Date;
18
- readonly accessed: Date;
19
- readonly permissions: string;
20
- readonly isHidden: boolean;
21
- readonly mimeType?: string;
22
- readonly symlinkTarget?: string;
23
- }
24
10
  export declare function isHidden(name: string): boolean;
25
11
  export declare class GuardedFileSystem {
26
12
  readonly pathGuard: PathGuard;