@j0hanz/filesystem-mcp 2.8.1 → 2.9.1
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 +33 -27
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +10 -1
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/concurrency.d.ts +4 -4
- package/dist/core/concurrency.d.ts.map +1 -1
- package/dist/core/concurrency.js +1 -1
- 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/errors.d.ts.map +1 -1
- package/dist/core/errors.js +7 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/file-uri.d.ts +12 -3
- package/dist/core/file-uri.d.ts.map +1 -1
- package/dist/core/file-uri.js +35 -7
- package/dist/core/file-uri.js.map +1 -1
- package/dist/core/fmt.d.ts +2 -0
- package/dist/core/fmt.d.ts.map +1 -1
- package/dist/core/fmt.js +16 -0
- package/dist/core/fmt.js.map +1 -1
- package/dist/core/fs.d.ts +16 -1
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +127 -32
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +15 -0
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +119 -14
- package/dist/core/glob.js.map +1 -1
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +5 -3
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/path-utils.d.ts +42 -6
- package/dist/core/path-utils.d.ts.map +1 -1
- package/dist/core/path-utils.js +56 -10
- package/dist/core/path-utils.js.map +1 -1
- package/dist/core/path.d.ts +111 -7
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +453 -65
- package/dist/core/path.js.map +1 -1
- package/dist/core/read.d.ts +6 -0
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +26 -3
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts +9 -6
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +22 -19
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +6 -0
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +161 -33
- package/dist/core/search.js.map +1 -1
- package/dist/core/sensitive.d.ts.map +1 -1
- package/dist/core/sensitive.js +64 -15
- package/dist/core/sensitive.js.map +1 -1
- package/dist/core/store.d.ts +1 -0
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +3 -0
- package/dist/core/store.js.map +1 -1
- package/dist/core/util.d.ts +12 -1
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +16 -1
- package/dist/core/util.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +20 -11
- package/dist/resources.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +121 -43
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +8 -4
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +36 -9
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete.d.ts.map +1 -1
- package/dist/tools/delete.js +50 -11
- package/dist/tools/delete.js.map +1 -1
- package/dist/tools/diff.js +1 -1
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +14 -12
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js +15 -9
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +141 -86
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +72 -26
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.js +13 -6
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +113 -48
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +50 -35
- package/dist/tools/replace-text.js.map +1 -1
- package/dist/tools/search-text.d.ts.map +1 -1
- package/dist/tools/search-text.js +15 -33
- package/dist/tools/search-text.js.map +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +26 -8
- package/dist/tools/stat.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +1 -0
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/stdio.js +1 -1
- package/dist/transport/stdio.js.map +1 -1
- package/package.json +5 -5
package/dist/core/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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","sourcesContent":["export const ErrorCode = {\n ACCESS_DENIED: 'ACCESS_DENIED',\n NOT_FOUND: 'NOT_FOUND',\n NOT_FILE: 'NOT_FILE',\n NOT_DIRECTORY: 'NOT_DIRECTORY',\n TOO_LARGE: 'TOO_LARGE',\n TIMEOUT: 'TIMEOUT',\n CANCELLED: 'CANCELLED',\n INVALID_PATTERN: 'INVALID_PATTERN',\n INVALID_INPUT: 'INVALID_INPUT',\n PERMISSION_DENIED: 'PERMISSION_DENIED',\n SYMLINK_NOT_ALLOWED: 'SYMLINK_NOT_ALLOWED',\n IO_ERROR: 'IO_ERROR',\n UNKNOWN: 'UNKNOWN',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nexport interface Problem {\n readonly code: ErrorCode;\n readonly message: string;\n readonly path?: string;\n readonly suggestion?: string;\n}\n\ninterface ProblemFactoryOptions {\n path?: string | undefined;\n suggestion?: string | undefined;\n}\n\nfunction build(code: ErrorCode, message: string, opts: ProblemFactoryOptions = {}): Problem {\n return {\n code,\n message,\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n ...(opts.suggestion !== undefined ? { suggestion: opts.suggestion } : {}),\n };\n}\n\nexport const Problem = {\n invalidInput: (msg: string, o?: ProblemFactoryOptions): Problem =>\n build(ErrorCode.INVALID_INPUT, msg, o),\n accessDenied: (msg: string, o?: ProblemFactoryOptions): Problem =>\n build(ErrorCode.ACCESS_DENIED, msg, o),\n cancelled: (msg: string, o?: ProblemFactoryOptions): Problem =>\n build(ErrorCode.CANCELLED, msg, o),\n fromUnknown(error: unknown, defaultCode: ErrorCode, path?: string): Problem {\n const problem = classify(error);\n const shouldOverride =\n problem.code === ErrorCode.UNKNOWN || problem.code === ErrorCode.IO_ERROR;\n const code = shouldOverride ? defaultCode : problem.code;\n const suggestion =\n (shouldOverride ? DEFAULT_SUGGESTIONS[code] : undefined) ??\n problem.suggestion ??\n DEFAULT_SUGGESTIONS[problem.code];\n return build(code, problem.message, { path: path ?? problem.path, suggestion });\n },\n toText(error: unknown, defaultCode: ErrorCode): string {\n return formatDetailedError(Problem.fromUnknown(error, defaultCode));\n },\n} as const;\n\nconst DEFAULT_SUGGESTIONS: Readonly<Partial<Record<ErrorCode, string>>> = {\n [ErrorCode.ACCESS_DENIED]: 'Run list_roots to list allowed directories.',\n [ErrorCode.NOT_FOUND]: 'Run list or find_files to verify the path.',\n [ErrorCode.NOT_FILE]: 'Target is a directory, not a file.',\n [ErrorCode.NOT_DIRECTORY]: 'Target is a file, not a directory.',\n [ErrorCode.TOO_LARGE]: 'Use head/tail or line ranges to read partially.',\n [ErrorCode.TIMEOUT]: 'Reduce scope, depth, or maxResults.',\n [ErrorCode.INVALID_PATTERN]: 'Check syntax and escape special characters.',\n [ErrorCode.PERMISSION_DENIED]: 'Check OS file permissions.',\n [ErrorCode.SYMLINK_NOT_ALLOWED]: 'Symlink escapes allowed directories.',\n};\n\nconst ERRNO_MAP: Readonly<Record<string, ErrorCode>> = {\n ENOENT: ErrorCode.NOT_FOUND,\n EACCES: ErrorCode.PERMISSION_DENIED,\n EPERM: ErrorCode.PERMISSION_DENIED,\n ENOTDIR: ErrorCode.NOT_DIRECTORY,\n EISDIR: ErrorCode.NOT_FILE,\n ELOOP: ErrorCode.SYMLINK_NOT_ALLOWED,\n ENAMETOOLONG: ErrorCode.INVALID_INPUT,\n ETIMEDOUT: ErrorCode.TIMEOUT,\n EMFILE: ErrorCode.IO_ERROR,\n ENFILE: ErrorCode.IO_ERROR,\n EBUSY: ErrorCode.IO_ERROR,\n ENOTEMPTY: ErrorCode.NOT_DIRECTORY,\n EEXIST: ErrorCode.INVALID_INPUT,\n EINVAL: ErrorCode.INVALID_INPUT,\n};\n\nconst ERRNO_RE = /^E[A-Z]+$/;\n\n// NOT_FOUND is in here (not just ENOENT) because a dangling symlink whose\n// target sits inside an allowed root surfaces as an FsError NOT_FOUND. Skipping\n// the entry is the point of a listing; rethrowing would fail the whole listing.\nexport const SKIPPABLE_FS_CODES: ReadonlySet<ErrorCode> = new Set([\n ErrorCode.ACCESS_DENIED,\n ErrorCode.NOT_FOUND,\n // OS-level EACCES/EPERM on a symlink target during a listing is exactly as\n // skippable as the other three codes.\n ErrorCode.PERMISSION_DENIED,\n ErrorCode.SYMLINK_NOT_ALLOWED,\n]);\n\nconst SKIPPABLE_ERRNOS: ReadonlySet<string> = new Set(['ENOENT', 'EACCES', 'ELOOP']);\n\n/**\n * Walk `error` and its `cause` chain once for the three facts error handling\n * needs: an abort anywhere, a timeout anywhere, and the FIRST errno seen.\n * `visited` guards a cyclic chain. A node that reads as an abort or a timeout\n * never also contributes its errno.\n */\nfunction scanCauseChain(error: unknown): {\n aborted: boolean;\n timedOut: boolean;\n errno: { code: string; path?: string } | undefined;\n} {\n const visited = new Set<unknown>();\n let aborted = false;\n let timedOut = false;\n let errno: { code: string; path?: string } | undefined;\n\n for (\n let current: unknown = error;\n current !== undefined && current !== null && !visited.has(current);\n current = (current as { cause?: unknown }).cause\n ) {\n visited.add(current);\n if (!(current instanceof Error)) continue;\n const raw = (current as { code?: unknown }).code;\n const code = typeof raw === 'string' ? raw : undefined;\n if (current.name === 'AbortError' || code === 'ABORT_ERR') {\n aborted = true;\n } else if (current.name === 'TimeoutError' || code === 'ETIMEDOUT') {\n timedOut = true;\n } else if (errno === undefined && code !== undefined && ERRNO_RE.test(code)) {\n const { path } = current as NodeJS.ErrnoException;\n errno = { code, ...(typeof path === 'string' ? { path } : {}) };\n }\n }\n\n return { aborted, timedOut, errno };\n}\n\n/** The scan's facts as a Problem: an abort wins, then a timeout, then errno. */\nexport function classifyCauseChain(error: unknown): Problem {\n const message = formatUnknownErrorMessage(error);\n const { aborted, timedOut, errno } = scanCauseChain(error);\n if (aborted) return Problem.cancelled(message);\n if (timedOut) return build(ErrorCode.TIMEOUT, message);\n if (errno !== undefined) {\n return build(ERRNO_MAP[errno.code] ?? ErrorCode.IO_ERROR, message, { path: errno.path });\n }\n return build(ErrorCode.UNKNOWN, message);\n}\n\nexport function isFsError(error: unknown): error is FsError {\n return error instanceof FsError;\n}\n\nfunction classify(error: unknown): Problem {\n if (error === null || error === undefined) {\n return build(ErrorCode.UNKNOWN, 'Unknown error');\n }\n if (isFsError(error)) return error.problem;\n // No `ZodError` branch, deliberately. Tool arguments are validated by the\n // SDK from the zod schema `defineTool` hands it (tools/define.ts); the SDK\n // formats issues itself and never throws a ZodError into a handler, and\n // nothing in `src/` calls `.parse()`. A ZodError reaching here would fall\n // through to UNKNOWN with Zod's raw JSON-dump `.message`, so if a\n // `.parse()` is ever added, add the branch back with it rather than\n // discovering this.\n if (!(error instanceof Error)) {\n return build(ErrorCode.UNKNOWN, typeof error === 'string' ? error : '[non-Error thrown]');\n }\n return classifyCauseChain(error);\n}\n\nexport function isNodeError(error: unknown): error is NodeJS.ErrnoException {\n if (!(error instanceof Error)) return false;\n if (!('code' in error)) return false;\n const { code } = error as { code?: unknown };\n return typeof code === 'string';\n}\n\n/** True when `error` is a Node errno error with code `ENOENT` (path not found). */\nexport function isNotFoundErrno(error: unknown): error is NodeJS.ErrnoException {\n return isNodeError(error) && error.code === 'ENOENT';\n}\n\n/** True when `error` is a Node errno error whose code is in `SKIPPABLE_ERRNOS`. */\nexport function isSkippableErrno(error: unknown): error is NodeJS.ErrnoException {\n return isNodeError(error) && error.code !== undefined && SKIPPABLE_ERRNOS.has(error.code);\n}\n\n/**\n * Rethrow when `error` reads as cancelled. Answers the question directly rather\n * than through `classify`, which allocated a Problem — and ran\n * `formatUnknownErrorMessage`, i.e. a `JSON.stringify` on a non-Error — for a\n * boolean. This sits in eight catch blocks, several inside per-entry scan loops.\n */\nexport function rethrowIfAborted(error: unknown): void {\n if (isFsError(error)) {\n if (error.code === ErrorCode.CANCELLED) throw error;\n return;\n }\n if (scanCauseChain(error).aborted) throw error;\n}\n\nexport function formatUnknownErrorMessage(error: unknown): string {\n if (typeof error === 'string') return error;\n if (error instanceof Error) return error.message;\n try {\n return JSON.stringify(error);\n } catch {\n return `[non-serializable: ${Object.prototype.toString.call(error)}]`;\n }\n}\n\nexport function normalizeUnknownError(error: unknown): Error {\n return error instanceof Error ? error : new Error(formatUnknownErrorMessage(error));\n}\n\nfunction formatDetailedError(\n error: Pick<Problem, 'code' | 'message' | 'path' | 'suggestion'>,\n): string {\n const lines: string[] = [`${error.code}: ${error.message}`];\n if (error.path && !error.message.includes(error.path)) {\n lines.push(error.path);\n }\n if (error.suggestion) {\n lines.push(error.suggestion);\n }\n return lines.join('\\n');\n}\n\nexport class FsError extends Error {\n readonly problem: Problem;\n\n /** `suggestion` replaces the code's default for a thrower that knows the better next step. */\n constructor(\n code: ErrorCode,\n message: string,\n path?: string,\n cause?: unknown,\n suggestion: string | undefined = DEFAULT_SUGGESTIONS[code],\n ) {\n super(message, cause === undefined ? {} : { cause });\n this.problem = build(code, message, { path, suggestion });\n this.name = 'FsError';\n }\n\n get code(): ErrorCode {\n return this.problem.code;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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","sourcesContent":["export const ErrorCode = {\n ACCESS_DENIED: 'ACCESS_DENIED',\n NOT_FOUND: 'NOT_FOUND',\n NOT_FILE: 'NOT_FILE',\n NOT_DIRECTORY: 'NOT_DIRECTORY',\n TOO_LARGE: 'TOO_LARGE',\n TIMEOUT: 'TIMEOUT',\n CANCELLED: 'CANCELLED',\n INVALID_PATTERN: 'INVALID_PATTERN',\n INVALID_INPUT: 'INVALID_INPUT',\n PERMISSION_DENIED: 'PERMISSION_DENIED',\n SYMLINK_NOT_ALLOWED: 'SYMLINK_NOT_ALLOWED',\n IO_ERROR: 'IO_ERROR',\n UNKNOWN: 'UNKNOWN',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nexport interface Problem {\n readonly code: ErrorCode;\n readonly message: string;\n readonly path?: string;\n readonly suggestion?: string;\n}\n\ninterface ProblemFactoryOptions {\n path?: string | undefined;\n suggestion?: string | undefined;\n}\n\nfunction build(code: ErrorCode, message: string, opts: ProblemFactoryOptions = {}): Problem {\n return {\n code,\n message,\n ...(opts.path !== undefined ? { path: opts.path } : {}),\n ...(opts.suggestion !== undefined ? { suggestion: opts.suggestion } : {}),\n };\n}\n\nexport const Problem = {\n invalidInput: (msg: string, o?: ProblemFactoryOptions): Problem =>\n build(ErrorCode.INVALID_INPUT, msg, o),\n accessDenied: (msg: string, o?: ProblemFactoryOptions): Problem =>\n build(ErrorCode.ACCESS_DENIED, msg, o),\n cancelled: (msg: string, o?: ProblemFactoryOptions): Problem =>\n build(ErrorCode.CANCELLED, msg, o),\n fromUnknown(error: unknown, defaultCode: ErrorCode, path?: string): Problem {\n const problem = classify(error);\n const shouldOverride =\n problem.code === ErrorCode.UNKNOWN || problem.code === ErrorCode.IO_ERROR;\n const code = shouldOverride ? defaultCode : problem.code;\n const suggestion =\n (shouldOverride ? DEFAULT_SUGGESTIONS[code] : undefined) ??\n problem.suggestion ??\n DEFAULT_SUGGESTIONS[problem.code];\n return build(code, problem.message, { path: path ?? problem.path, suggestion });\n },\n toText(error: unknown, defaultCode: ErrorCode): string {\n return formatDetailedError(Problem.fromUnknown(error, defaultCode));\n },\n} as const;\n\nconst DEFAULT_SUGGESTIONS: Readonly<Partial<Record<ErrorCode, string>>> = {\n [ErrorCode.ACCESS_DENIED]: 'Run list_roots to list allowed directories.',\n [ErrorCode.NOT_FOUND]: 'Run list or find_files to verify the path.',\n [ErrorCode.NOT_FILE]: 'Target is a directory, not a file.',\n [ErrorCode.NOT_DIRECTORY]: 'Target is a file, not a directory.',\n [ErrorCode.TOO_LARGE]: 'Use head/tail or line ranges to read partially.',\n [ErrorCode.TIMEOUT]: 'Reduce scope, depth, or maxResults.',\n [ErrorCode.INVALID_PATTERN]: 'Check syntax and escape special characters.',\n [ErrorCode.PERMISSION_DENIED]: 'Check OS file permissions.',\n [ErrorCode.SYMLINK_NOT_ALLOWED]: 'Symlink escapes allowed directories.',\n};\n\nconst ERRNO_MAP: Readonly<Record<string, ErrorCode>> = {\n ENOENT: ErrorCode.NOT_FOUND,\n EACCES: ErrorCode.PERMISSION_DENIED,\n EPERM: ErrorCode.PERMISSION_DENIED,\n ENOTDIR: ErrorCode.NOT_DIRECTORY,\n EISDIR: ErrorCode.NOT_FILE,\n ELOOP: ErrorCode.SYMLINK_NOT_ALLOWED,\n ENAMETOOLONG: ErrorCode.INVALID_INPUT,\n ETIMEDOUT: ErrorCode.TIMEOUT,\n EMFILE: ErrorCode.IO_ERROR,\n ENFILE: ErrorCode.IO_ERROR,\n EBUSY: ErrorCode.IO_ERROR,\n ENOTEMPTY: ErrorCode.NOT_DIRECTORY,\n EEXIST: ErrorCode.INVALID_INPUT,\n EINVAL: ErrorCode.INVALID_INPUT,\n};\n\nconst ERRNO_RE = /^E[A-Z]+$/;\n\n// NOT_FOUND is in here (not just ENOENT) because a dangling symlink whose\n// target sits inside an allowed root surfaces as an FsError NOT_FOUND. Skipping\n// the entry is the point of a listing; rethrowing would fail the whole listing.\nexport const SKIPPABLE_FS_CODES: ReadonlySet<ErrorCode> = new Set([\n ErrorCode.ACCESS_DENIED,\n ErrorCode.NOT_FOUND,\n // OS-level EACCES/EPERM on a symlink target during a listing is exactly as\n // skippable as the other three codes.\n ErrorCode.PERMISSION_DENIED,\n ErrorCode.SYMLINK_NOT_ALLOWED,\n]);\n\n// Derived, not hand-maintained: an errno is skippable exactly when the code\n// ERRNO_MAP classifies it as a code in SKIPPABLE_FS_CODES. EPERM maps to\n// PERMISSION_DENIED, so OS-level EPERM is as skippable as EACCES — the hand-\n// maintained list above omitted it and diverged from its own comment.\nconst SKIPPABLE_ERRNOS: ReadonlySet<string> = new Set(\n Object.entries(ERRNO_MAP)\n .filter(([, code]) => SKIPPABLE_FS_CODES.has(code))\n .map(([errno]) => errno),\n);\n\n/**\n * Walk `error` and its `cause` chain once for the three facts error handling\n * needs: an abort anywhere, a timeout anywhere, and the FIRST errno seen.\n * `visited` guards a cyclic chain. A node that reads as an abort or a timeout\n * never also contributes its errno.\n */\nfunction scanCauseChain(error: unknown): {\n aborted: boolean;\n timedOut: boolean;\n errno: { code: string; path?: string } | undefined;\n} {\n const visited = new Set<unknown>();\n let aborted = false;\n let timedOut = false;\n let errno: { code: string; path?: string } | undefined;\n\n for (\n let current: unknown = error;\n current !== undefined && current !== null && !visited.has(current);\n current = (current as { cause?: unknown }).cause\n ) {\n visited.add(current);\n if (!(current instanceof Error)) continue;\n const raw = (current as { code?: unknown }).code;\n const code = typeof raw === 'string' ? raw : undefined;\n if (current.name === 'AbortError' || code === 'ABORT_ERR') {\n aborted = true;\n } else if (current.name === 'TimeoutError' || code === 'ETIMEDOUT') {\n timedOut = true;\n } else if (errno === undefined && code !== undefined && ERRNO_RE.test(code)) {\n const { path } = current as NodeJS.ErrnoException;\n errno = { code, ...(typeof path === 'string' ? { path } : {}) };\n }\n }\n\n return { aborted, timedOut, errno };\n}\n\n/** The scan's facts as a Problem: an abort wins, then a timeout, then errno. */\nexport function classifyCauseChain(error: unknown): Problem {\n const message = formatUnknownErrorMessage(error);\n const { aborted, timedOut, errno } = scanCauseChain(error);\n if (aborted) return Problem.cancelled(message);\n if (timedOut) return build(ErrorCode.TIMEOUT, message);\n if (errno !== undefined) {\n return build(ERRNO_MAP[errno.code] ?? ErrorCode.IO_ERROR, message, { path: errno.path });\n }\n return build(ErrorCode.UNKNOWN, message);\n}\n\nexport function isFsError(error: unknown): error is FsError {\n return error instanceof FsError;\n}\n\nfunction classify(error: unknown): Problem {\n if (error === null || error === undefined) {\n return build(ErrorCode.UNKNOWN, 'Unknown error');\n }\n if (isFsError(error)) return error.problem;\n // No `ZodError` branch, deliberately. Tool arguments are validated by the\n // SDK from the zod schema `defineTool` hands it (tools/define.ts); the SDK\n // formats issues itself and never throws a ZodError into a handler, and\n // nothing in `src/` calls `.parse()`. A ZodError reaching here would fall\n // through to UNKNOWN with Zod's raw JSON-dump `.message`, so if a\n // `.parse()` is ever added, add the branch back with it rather than\n // discovering this.\n if (!(error instanceof Error)) {\n return build(ErrorCode.UNKNOWN, typeof error === 'string' ? error : '[non-Error thrown]');\n }\n return classifyCauseChain(error);\n}\n\nexport function isNodeError(error: unknown): error is NodeJS.ErrnoException {\n if (!(error instanceof Error)) return false;\n if (!('code' in error)) return false;\n const { code } = error as { code?: unknown };\n return typeof code === 'string';\n}\n\n/** True when `error` is a Node errno error with code `ENOENT` (path not found). */\nexport function isNotFoundErrno(error: unknown): error is NodeJS.ErrnoException {\n return isNodeError(error) && error.code === 'ENOENT';\n}\n\n/** True when `error` is a Node errno error whose code is in `SKIPPABLE_ERRNOS`. */\nexport function isSkippableErrno(error: unknown): error is NodeJS.ErrnoException {\n return isNodeError(error) && error.code !== undefined && SKIPPABLE_ERRNOS.has(error.code);\n}\n\n/**\n * Rethrow when `error` reads as cancelled. Answers the question directly rather\n * than through `classify`, which allocated a Problem — and ran\n * `formatUnknownErrorMessage`, i.e. a `JSON.stringify` on a non-Error — for a\n * boolean. This sits in eight catch blocks, several inside per-entry scan loops.\n */\nexport function rethrowIfAborted(error: unknown): void {\n if (isFsError(error)) {\n if (error.code === ErrorCode.CANCELLED) throw error;\n return;\n }\n if (scanCauseChain(error).aborted) throw error;\n}\n\nexport function formatUnknownErrorMessage(error: unknown): string {\n if (typeof error === 'string') return error;\n if (error instanceof Error) return error.message;\n try {\n return JSON.stringify(error);\n } catch {\n return `[non-serializable: ${Object.prototype.toString.call(error)}]`;\n }\n}\n\nexport function normalizeUnknownError(error: unknown): Error {\n return error instanceof Error ? error : new Error(formatUnknownErrorMessage(error));\n}\n\nfunction formatDetailedError(\n error: Pick<Problem, 'code' | 'message' | 'path' | 'suggestion'>,\n): string {\n const lines: string[] = [`${error.code}: ${error.message}`];\n if (error.path && !error.message.includes(error.path)) {\n lines.push(error.path);\n }\n if (error.suggestion) {\n lines.push(error.suggestion);\n }\n return lines.join('\\n');\n}\n\nexport class FsError extends Error {\n readonly problem: Problem;\n\n /** `suggestion` replaces the code's default for a thrower that knows the better next step. */\n constructor(\n code: ErrorCode,\n message: string,\n path?: string,\n cause?: unknown,\n suggestion: string | undefined = DEFAULT_SUGGESTIONS[code],\n ) {\n super(message, cause === undefined ? {} : { cause });\n this.problem = build(code, message, { path, suggestion });\n this.name = 'FsError';\n }\n\n get code(): ErrorCode {\n return this.problem.code;\n }\n}\n"]}
|
package/dist/core/file-uri.d.ts
CHANGED
|
@@ -6,7 +6,9 @@ export declare const FILESYSTEM_FILE_URI_TEMPLATE = "filesystem-mcp://file/{+pat
|
|
|
6
6
|
* must return, since a client expands a suggestion into the template verbatim.
|
|
7
7
|
* Unescaped, a '#' or '?' in a filename truncates the URI into a fragment or
|
|
8
8
|
* query (silently naming a different path) and a '%' makes the decode throw.
|
|
9
|
-
* Separators are restored so the value still reads as a path.
|
|
9
|
+
* Separators are restored so the value still reads as a path. A backslash is
|
|
10
|
+
* a separator only on Windows; on POSIX it is part of a name and stays `%5C`,
|
|
11
|
+
* or `a\b.txt` and `a/b.txt` would share one URI.
|
|
10
12
|
*/
|
|
11
13
|
export declare function encodeFileUriPath(validPath: string): string;
|
|
12
14
|
/** Inverse of {@link encodeFileUriPath}; `undefined` on invalid percent-encoding. */
|
|
@@ -40,9 +42,15 @@ export interface WrittenFileMeta {
|
|
|
40
42
|
* the store serves the URI via readRaw, which would reject it with TOO_LARGE.
|
|
41
43
|
* File resources are read directly from disk, not from the result store.
|
|
42
44
|
* `lastModified` is the post-write mtime when the caller stat'd the file; a
|
|
43
|
-
* link without it is still valid.
|
|
45
|
+
* link without it is still valid. None over stdio when the read would refuse
|
|
46
|
+
* it: the JSON-encoded result past `MAX_OUTBOUND_RESULT_BYTES` (escaping
|
|
47
|
+
* counts, binary is base64), or, with no content to measure, any file whose
|
|
48
|
+
* worst-case escape could be.
|
|
44
49
|
*/
|
|
45
|
-
export declare function writtenFileLinks(validPath: string, mimeType: string, size: number | undefined, dryRun?: boolean, lastModified?: string
|
|
50
|
+
export declare function writtenFileLinks(validPath: string, mimeType: string, size: number | undefined, dryRun?: boolean, lastModified?: string, transport?: 'stdio' | 'http', written?: {
|
|
51
|
+
content: string;
|
|
52
|
+
kind: FileKind;
|
|
53
|
+
}): Pick<WrittenFileMeta, 'resourceUri' | 'resourceLink'>;
|
|
46
54
|
/**
|
|
47
55
|
* The block every write tool reports for the content it wrote (or, with
|
|
48
56
|
* `dryRun`, would have written): size, line count, MIME, and whatever
|
|
@@ -54,5 +62,6 @@ export declare function buildWrittenFileMeta(options: {
|
|
|
54
62
|
content: string;
|
|
55
63
|
dryRun?: boolean | undefined;
|
|
56
64
|
lastModified?: string | undefined;
|
|
65
|
+
transport?: 'stdio' | 'http' | undefined;
|
|
57
66
|
}): WrittenFileMeta;
|
|
58
67
|
//# sourceMappingURL=file-uri.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-uri.d.ts","sourceRoot":"","sources":["../../src/core/file-uri.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAKjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"file-uri.d.ts","sourceRoot":"","sources":["../../src/core/file-uri.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAKjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAgB1C,eAAO,MAAM,4BAA4B,kCAAkC,CAAC;AAE5E;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMrE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM;AACZ,0EAA0E;AAC1E,YAAY,CAAC,EAAE,MAAM,GACpB,YAAY,CAYd;AAiBD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI3D;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,+EAA+E;IAC/E,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,+EAA+E;IAC/E,YAAY,EAAE,YAAY,GAAG,SAAS,CAAC;CACxC;AAyBD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,MAAM,UAAQ,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,EAC5B,OAAO,CAAC,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC5C,IAAI,CAAC,eAAe,EAAE,aAAa,GAAG,cAAc,CAAC,CAavD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CAC1C,GAAG,eAAe,CAmBlB"}
|
package/dist/core/file-uri.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
2
|
import { detectMimeFromContent } from "./mime.js";
|
|
3
|
+
import { IS_WINDOWS, toPosixPath } from "./path-utils.js";
|
|
3
4
|
import { countLines } from "./read.js";
|
|
4
|
-
import { getMaxTextFileSize } from "./util.js";
|
|
5
|
+
import { base64EncodedBytes, getMaxTextFileSize, jsonBytesOver, MAX_OUTBOUND_RESULT_BYTES, } from "./util.js";
|
|
6
|
+
const KIB = 1024;
|
|
5
7
|
// Single owner of the `filesystem-mcp://file/` URI scheme — the template string,
|
|
6
8
|
// the path→URI encoder, the URI→path decoder, the link blocks built from them,
|
|
7
9
|
// and the post-write metadata block every write tool reports alongside them.
|
|
@@ -11,10 +13,12 @@ export const FILESYSTEM_FILE_URI_TEMPLATE = 'filesystem-mcp://file/{+path}';
|
|
|
11
13
|
* must return, since a client expands a suggestion into the template verbatim.
|
|
12
14
|
* Unescaped, a '#' or '?' in a filename truncates the URI into a fragment or
|
|
13
15
|
* query (silently naming a different path) and a '%' makes the decode throw.
|
|
14
|
-
* Separators are restored so the value still reads as a path.
|
|
16
|
+
* Separators are restored so the value still reads as a path. A backslash is
|
|
17
|
+
* a separator only on Windows; on POSIX it is part of a name and stays `%5C`,
|
|
18
|
+
* or `a\b.txt` and `a/b.txt` would share one URI.
|
|
15
19
|
*/
|
|
16
20
|
export function encodeFileUriPath(validPath) {
|
|
17
|
-
const posix = validPath
|
|
21
|
+
const posix = IS_WINDOWS ? toPosixPath(validPath) : validPath;
|
|
18
22
|
return encodeURIComponent(posix).replace(/%2F/gi, '/');
|
|
19
23
|
}
|
|
20
24
|
/** Inverse of {@link encodeFileUriPath}; `undefined` on invalid percent-encoding. */
|
|
@@ -60,6 +64,24 @@ export function extractPath(uri) {
|
|
|
60
64
|
return undefined;
|
|
61
65
|
return decodeFileUriPath(url.pathname.slice(1));
|
|
62
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether stdio's resources/read would refuse this file: it measures the
|
|
69
|
+
* JSON-encoded result, so escaping counts, and a file it reads as binary
|
|
70
|
+
* comes back base64. Without the content, assume the worst escape (6 bytes
|
|
71
|
+
* per byte) so a link is never one the read refuses.
|
|
72
|
+
*/
|
|
73
|
+
function overStdioRead(validPath, mimeType, size, written) {
|
|
74
|
+
if (written === undefined)
|
|
75
|
+
return 6 * size > MAX_OUTBOUND_RESULT_BYTES - 64 * KIB;
|
|
76
|
+
// readRaw returns any non-text kind as base64 (`isBinary: kind !== 'text'`).
|
|
77
|
+
if (written.kind !== 'text') {
|
|
78
|
+
return base64EncodedBytes(size) > MAX_OUTBOUND_RESULT_BYTES - 64 * KIB;
|
|
79
|
+
}
|
|
80
|
+
const result = {
|
|
81
|
+
contents: [{ uri: buildFileResourceUri(validPath), mimeType, text: written.content }],
|
|
82
|
+
};
|
|
83
|
+
return jsonBytesOver(result, MAX_OUTBOUND_RESULT_BYTES) !== undefined;
|
|
84
|
+
}
|
|
63
85
|
/**
|
|
64
86
|
* The URI and link a write tool may advertise for the file it wrote. None on a
|
|
65
87
|
* dry run — nothing was written, so the file on disk is still the one the
|
|
@@ -67,10 +89,16 @@ export function extractPath(uri) {
|
|
|
67
89
|
* the store serves the URI via readRaw, which would reject it with TOO_LARGE.
|
|
68
90
|
* File resources are read directly from disk, not from the result store.
|
|
69
91
|
* `lastModified` is the post-write mtime when the caller stat'd the file; a
|
|
70
|
-
* link without it is still valid.
|
|
92
|
+
* link without it is still valid. None over stdio when the read would refuse
|
|
93
|
+
* it: the JSON-encoded result past `MAX_OUTBOUND_RESULT_BYTES` (escaping
|
|
94
|
+
* counts, binary is base64), or, with no content to measure, any file whose
|
|
95
|
+
* worst-case escape could be.
|
|
71
96
|
*/
|
|
72
|
-
export function writtenFileLinks(validPath, mimeType, size, dryRun = false, lastModified) {
|
|
73
|
-
if (dryRun ||
|
|
97
|
+
export function writtenFileLinks(validPath, mimeType, size, dryRun = false, lastModified, transport, written) {
|
|
98
|
+
if (dryRun ||
|
|
99
|
+
size === undefined ||
|
|
100
|
+
size > getMaxTextFileSize() ||
|
|
101
|
+
(transport === 'stdio' && overStdioRead(validPath, mimeType, size, written))) {
|
|
74
102
|
return { resourceUri: undefined, resourceLink: undefined };
|
|
75
103
|
}
|
|
76
104
|
return {
|
|
@@ -93,7 +121,7 @@ export function buildWrittenFileMeta(options) {
|
|
|
93
121
|
lineCount: countLines(content),
|
|
94
122
|
mimeType,
|
|
95
123
|
kind,
|
|
96
|
-
...writtenFileLinks(validPath, mimeType, size, options.dryRun, options.lastModified),
|
|
124
|
+
...writtenFileLinks(validPath, mimeType, size, options.dryRun, options.lastModified, options.transport, { content, kind }),
|
|
97
125
|
};
|
|
98
126
|
}
|
|
99
127
|
//# sourceMappingURL=file-uri.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-uri.js","sourceRoot":"","sources":["../../src/core/file-uri.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport { detectMimeFromContent } from './mime.ts';\nimport type { FileKind } from './mime.ts';\nimport { countLines } from './read.ts';\nimport { getMaxTextFileSize } from './util.ts';\n\n// Single owner of the `filesystem-mcp://file/` URI scheme — the template string,\n// the path→URI encoder, the URI→path decoder, the link blocks built from them,\n// and the post-write metadata block every write tool reports alongside them.\n\nexport const FILESYSTEM_FILE_URI_TEMPLATE = 'filesystem-mcp://file/{+path}';\n\n/**\n * The `{+path}` template-variable form of a path — what `completion/complete`\n * must return, since a client expands a suggestion into the template verbatim.\n * Unescaped, a '#' or '?' in a filename truncates the URI into a fragment or\n * query (silently naming a different path) and a '%' makes the decode throw.\n * Separators are restored so the value still reads as a path.\n */\nexport function encodeFileUriPath(validPath: string): string {\n const posix = validPath.replace(/\\\\/g, '/');\n return encodeURIComponent(posix).replace(/%2F/gi, '/');\n}\n\n/** Inverse of {@link encodeFileUriPath}; `undefined` on invalid percent-encoding. */\nexport function decodeFileUriPath(encoded: string): string | undefined {\n try {\n return decodeURIComponent(encoded);\n } catch {\n return undefined;\n }\n}\n\nexport function buildFileResourceUri(validPath: string): string {\n return `filesystem-mcp://file/${encodeFileUriPath(validPath)}`;\n}\n\n/**\n * The link block for a URI already built by `buildFileResourceUri`. Callers that\n * hold the URI must use this rather than rebuilding from a path: a path that\n * differs only in case (the drive letter, on Windows) yields a *different* URI\n * string, and watchers key on that string — a link and a `resourceUri` that\n * disagree hand the client two subscriptions for one file.\n */\nexport function buildFileResourceLinkFor(\n uri: string,\n name: string,\n mimeType: string,\n size: number,\n /** ISO-8601 mtime of the file the link names, when the caller has one. */\n lastModified?: string,\n): ContentBlock {\n return {\n type: 'resource_link',\n uri,\n name,\n mimeType,\n size,\n annotations: {\n audience: ['user', 'assistant'],\n ...(lastModified !== undefined ? { lastModified } : {}),\n },\n };\n}\n\nfunction buildFileResourceLink(\n validPath: string,\n mimeType: string,\n size: number,\n lastModified?: string,\n): ContentBlock {\n return buildFileResourceLinkFor(\n buildFileResourceUri(validPath),\n basename(validPath),\n mimeType,\n size,\n lastModified,\n );\n}\n\nexport function extractPath(uri: string): string | undefined {\n const url = URL.parse(uri);\n if (url?.protocol !== 'filesystem-mcp:' || url.host !== 'file') return undefined;\n return decodeFileUriPath(url.pathname.slice(1));\n}\n\nexport interface WrittenFileMeta {\n size: number;\n lineCount: number;\n mimeType: string;\n kind: FileKind;\n /** Undefined unless {@link writtenFileLinks} allows it: see there for when. */\n resourceUri: string | undefined;\n /** Undefined unless {@link writtenFileLinks} allows it: see there for when. */\n resourceLink: ContentBlock | undefined;\n}\n\n/**\n * The URI and link a write tool may advertise for the file it wrote. None on a\n * dry run — nothing was written, so the file on disk is still the one the\n * caller already has. None when the size is unknown or over the text-size cap —\n * the store serves the URI via readRaw, which would reject it with TOO_LARGE.\n * File resources are read directly from disk, not from the result store.\n * `lastModified` is the post-write mtime when the caller stat'd the file; a\n * link without it is still valid.\n */\nexport function writtenFileLinks(\n validPath: string,\n mimeType: string,\n size: number | undefined,\n dryRun = false,\n lastModified?: string,\n): Pick<WrittenFileMeta, 'resourceUri' | 'resourceLink'> {\n if (dryRun || size === undefined || size > getMaxTextFileSize()) {\n return { resourceUri: undefined, resourceLink: undefined };\n }\n return {\n resourceUri: buildFileResourceUri(validPath),\n resourceLink: buildFileResourceLink(validPath, mimeType, size, lastModified),\n };\n}\n\n/**\n * The block every write tool reports for the content it wrote (or, with\n * `dryRun`, would have written): size, line count, MIME, and whatever\n * {@link writtenFileLinks} allows it to advertise. Named fields, because\n * `validPath` and `content` are both strings and a positional swap type-checks.\n */\nexport function buildWrittenFileMeta(options: {\n validPath: string;\n content: string;\n dryRun?: boolean | undefined;\n lastModified?: string | undefined;\n}): WrittenFileMeta {\n const { validPath, content } = options;\n const size = Buffer.byteLength(content, 'utf-8');\n const { mimeType, kind } = detectMimeFromContent(validPath, content);\n return {\n size,\n lineCount: countLines(content),\n mimeType,\n kind,\n ...writtenFileLinks(validPath, mimeType, size, options.dryRun, options.lastModified),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"file-uri.js","sourceRoot":"","sources":["../../src/core/file-uri.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport { detectMimeFromContent } from './mime.ts';\nimport type { FileKind } from './mime.ts';\nimport { IS_WINDOWS, toPosixPath } from './path-utils.ts';\nimport { countLines } from './read.ts';\nimport {\n base64EncodedBytes,\n getMaxTextFileSize,\n jsonBytesOver,\n MAX_OUTBOUND_RESULT_BYTES,\n} from './util.ts';\n\nconst KIB = 1024;\n\n// Single owner of the `filesystem-mcp://file/` URI scheme — the template string,\n// the path→URI encoder, the URI→path decoder, the link blocks built from them,\n// and the post-write metadata block every write tool reports alongside them.\n\nexport const FILESYSTEM_FILE_URI_TEMPLATE = 'filesystem-mcp://file/{+path}';\n\n/**\n * The `{+path}` template-variable form of a path — what `completion/complete`\n * must return, since a client expands a suggestion into the template verbatim.\n * Unescaped, a '#' or '?' in a filename truncates the URI into a fragment or\n * query (silently naming a different path) and a '%' makes the decode throw.\n * Separators are restored so the value still reads as a path. A backslash is\n * a separator only on Windows; on POSIX it is part of a name and stays `%5C`,\n * or `a\\b.txt` and `a/b.txt` would share one URI.\n */\nexport function encodeFileUriPath(validPath: string): string {\n const posix = IS_WINDOWS ? toPosixPath(validPath) : validPath;\n return encodeURIComponent(posix).replace(/%2F/gi, '/');\n}\n\n/** Inverse of {@link encodeFileUriPath}; `undefined` on invalid percent-encoding. */\nexport function decodeFileUriPath(encoded: string): string | undefined {\n try {\n return decodeURIComponent(encoded);\n } catch {\n return undefined;\n }\n}\n\nexport function buildFileResourceUri(validPath: string): string {\n return `filesystem-mcp://file/${encodeFileUriPath(validPath)}`;\n}\n\n/**\n * The link block for a URI already built by `buildFileResourceUri`. Callers that\n * hold the URI must use this rather than rebuilding from a path: a path that\n * differs only in case (the drive letter, on Windows) yields a *different* URI\n * string, and watchers key on that string — a link and a `resourceUri` that\n * disagree hand the client two subscriptions for one file.\n */\nexport function buildFileResourceLinkFor(\n uri: string,\n name: string,\n mimeType: string,\n size: number,\n /** ISO-8601 mtime of the file the link names, when the caller has one. */\n lastModified?: string,\n): ContentBlock {\n return {\n type: 'resource_link',\n uri,\n name,\n mimeType,\n size,\n annotations: {\n audience: ['user', 'assistant'],\n ...(lastModified !== undefined ? { lastModified } : {}),\n },\n };\n}\n\nfunction buildFileResourceLink(\n validPath: string,\n mimeType: string,\n size: number,\n lastModified?: string,\n): ContentBlock {\n return buildFileResourceLinkFor(\n buildFileResourceUri(validPath),\n basename(validPath),\n mimeType,\n size,\n lastModified,\n );\n}\n\nexport function extractPath(uri: string): string | undefined {\n const url = URL.parse(uri);\n if (url?.protocol !== 'filesystem-mcp:' || url.host !== 'file') return undefined;\n return decodeFileUriPath(url.pathname.slice(1));\n}\n\nexport interface WrittenFileMeta {\n size: number;\n lineCount: number;\n mimeType: string;\n kind: FileKind;\n /** Undefined unless {@link writtenFileLinks} allows it: see there for when. */\n resourceUri: string | undefined;\n /** Undefined unless {@link writtenFileLinks} allows it: see there for when. */\n resourceLink: ContentBlock | undefined;\n}\n\n/**\n * Whether stdio's resources/read would refuse this file: it measures the\n * JSON-encoded result, so escaping counts, and a file it reads as binary\n * comes back base64. Without the content, assume the worst escape (6 bytes\n * per byte) so a link is never one the read refuses.\n */\nfunction overStdioRead(\n validPath: string,\n mimeType: string,\n size: number,\n written: { content: string; kind: FileKind } | undefined,\n): boolean {\n if (written === undefined) return 6 * size > MAX_OUTBOUND_RESULT_BYTES - 64 * KIB;\n // readRaw returns any non-text kind as base64 (`isBinary: kind !== 'text'`).\n if (written.kind !== 'text') {\n return base64EncodedBytes(size) > MAX_OUTBOUND_RESULT_BYTES - 64 * KIB;\n }\n const result = {\n contents: [{ uri: buildFileResourceUri(validPath), mimeType, text: written.content }],\n };\n return jsonBytesOver(result, MAX_OUTBOUND_RESULT_BYTES) !== undefined;\n}\n\n/**\n * The URI and link a write tool may advertise for the file it wrote. None on a\n * dry run — nothing was written, so the file on disk is still the one the\n * caller already has. None when the size is unknown or over the text-size cap —\n * the store serves the URI via readRaw, which would reject it with TOO_LARGE.\n * File resources are read directly from disk, not from the result store.\n * `lastModified` is the post-write mtime when the caller stat'd the file; a\n * link without it is still valid. None over stdio when the read would refuse\n * it: the JSON-encoded result past `MAX_OUTBOUND_RESULT_BYTES` (escaping\n * counts, binary is base64), or, with no content to measure, any file whose\n * worst-case escape could be.\n */\nexport function writtenFileLinks(\n validPath: string,\n mimeType: string,\n size: number | undefined,\n dryRun = false,\n lastModified?: string,\n transport?: 'stdio' | 'http',\n written?: { content: string; kind: FileKind },\n): Pick<WrittenFileMeta, 'resourceUri' | 'resourceLink'> {\n if (\n dryRun ||\n size === undefined ||\n size > getMaxTextFileSize() ||\n (transport === 'stdio' && overStdioRead(validPath, mimeType, size, written))\n ) {\n return { resourceUri: undefined, resourceLink: undefined };\n }\n return {\n resourceUri: buildFileResourceUri(validPath),\n resourceLink: buildFileResourceLink(validPath, mimeType, size, lastModified),\n };\n}\n\n/**\n * The block every write tool reports for the content it wrote (or, with\n * `dryRun`, would have written): size, line count, MIME, and whatever\n * {@link writtenFileLinks} allows it to advertise. Named fields, because\n * `validPath` and `content` are both strings and a positional swap type-checks.\n */\nexport function buildWrittenFileMeta(options: {\n validPath: string;\n content: string;\n dryRun?: boolean | undefined;\n lastModified?: string | undefined;\n transport?: 'stdio' | 'http' | undefined;\n}): WrittenFileMeta {\n const { validPath, content } = options;\n const size = Buffer.byteLength(content, 'utf-8');\n const { mimeType, kind } = detectMimeFromContent(validPath, content);\n return {\n size,\n lineCount: countLines(content),\n mimeType,\n kind,\n ...writtenFileLinks(\n validPath,\n mimeType,\n size,\n options.dryRun,\n options.lastModified,\n options.transport,\n { content, kind },\n ),\n };\n}\n"]}
|
package/dist/core/fmt.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface ProgressCtx {
|
|
|
10
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
|
+
/** Human-readable byte size for tree lines and summary text: `5 B`, `1 KiB`, `1.5 KiB`, `20 KiB`. */
|
|
14
|
+
export declare function formatByteSize(bytes: number): string;
|
|
13
15
|
/**
|
|
14
16
|
* Short label for a path in summary text. `basename` returns '' for a root
|
|
15
17
|
* (`C:\`, `/`) and for a trailing-slash path, which rendered as a bare
|
package/dist/core/fmt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fmt.d.ts","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"AAIA,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,GAAG,MAAM,CAE/D;AAYD;;;;;;;;;;GAUG;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,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,GAAG,MAAM,CAsBT;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAG9D;AASD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACrB,GAAG,MAAM,GAAG,SAAS,CAWrB"}
|
|
1
|
+
{"version":3,"file":"fmt.d.ts","sourceRoot":"","sources":["../../src/core/fmt.ts"],"names":[],"mappings":"AAIA,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;AAID,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAapD;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,GAAG,MAAM,CAE/D;AAYD;;;;;;;;;;GAUG;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,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,GAAG,MAAM,CAsBT;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAG9D;AASD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACrB,GAAG,MAAM,GAAG,SAAS,CAWrB"}
|
package/dist/core/fmt.js
CHANGED
|
@@ -37,6 +37,22 @@ export function plainMessage(phase, ctx) {
|
|
|
37
37
|
export function formatCount(count, singular, plural = `${singular}s`) {
|
|
38
38
|
return `${String(count)} ${count === 1 ? singular : plural}`;
|
|
39
39
|
}
|
|
40
|
+
const SIZE_UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
|
41
|
+
/** Human-readable byte size for tree lines and summary text: `5 B`, `1 KiB`, `1.5 KiB`, `20 KiB`. */
|
|
42
|
+
export function formatByteSize(bytes) {
|
|
43
|
+
let value = bytes;
|
|
44
|
+
let unit = 0;
|
|
45
|
+
while (value >= 1024 && unit < SIZE_UNITS.length - 1) {
|
|
46
|
+
value /= 1024;
|
|
47
|
+
unit++;
|
|
48
|
+
}
|
|
49
|
+
let rounded = unit === 0 || value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
50
|
+
if (rounded >= 1024 && unit < SIZE_UNITS.length - 1) {
|
|
51
|
+
rounded = 1;
|
|
52
|
+
unit++;
|
|
53
|
+
}
|
|
54
|
+
return `${String(rounded)} ${SIZE_UNITS[unit]}`;
|
|
55
|
+
}
|
|
40
56
|
/**
|
|
41
57
|
* Short label for a path in summary text. `basename` returns '' for a root
|
|
42
58
|
* (`C:\`, `/`) and for a trailing-slash path, which rendered as a bare
|
package/dist/core/fmt.js.map
CHANGED
|
@@ -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';\n\nimport { MAX_SEARCH_RESULTS } from './util.ts';\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): string {\n return pattern.length <= 40 ? pattern : `${pattern.slice(0, 40)}…`;\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, the\n * engine's stop state and the files it skipped live in the structured half,\n * which `defineTool` ships under `_meta` for a tool that authors its own text —\n * and no client renders that. These lines are the only place a caller learns\n * that more remains, that the scan was cut, or that files went unexamined (a\n * lone match in an oversized file otherwise reads as \"No matches\"). They are\n * independent: a scan can stop early with too few results to page, so a\n * truncation with no cursor must still say so. Prefixed `//` so none can be\n * 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 /** The call's own arguments; printed merged with the cursor so the hint is sendable as-is. */\n nextArgs?: Record<string, unknown> | undefined;\n stoppedReason?: string | undefined;\n skippedTooLarge?: number | undefined;\n skippedInaccessible?: number | 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({ ...p.nextArgs, 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) lines.push(stoppedEarlyLine(p.stoppedReason));\n const skipped = [\n p.skippedTooLarge ? `${formatCount(p.skippedTooLarge, 'file')} over the text size limit` : '',\n p.skippedInaccessible ? formatCount(p.skippedInaccessible, 'inaccessible file') : '',\n ].filter(Boolean);\n if (skipped.length > 0) {\n lines.push(`// skipped ${skipped.join(', ')}; results may be incomplete.`);\n }\n return lines.length > 0 ? `\\n\\n${lines.join('\\n')}` : '';\n}\n\n/** The `//` line saying a scan was cut short. */\nexport function stoppedEarlyLine(stoppedReason: string): string {\n const why = STOP_REASON_TEXT[stoppedReason] ?? `stopped (${stoppedReason})`;\n return `// scan stopped early: ${why}. That total is a floor, not the count. Narrow path or pattern for the rest.`;\n}\n\n// Regex syntax literal code rarely holds: unspaced alternation (`a|b`, not\n// `||` or a `string | null` union), class escapes, and `.*` / `.+`. Bare `(`,\n// `.`, `[` stay out — `foo(` and `a.b` are ordinary literal needles. A Windows\n// path like `src\\db` can still trip `\\d`; the hint only shows on zero\n// matches, so a stray one costs one line.\nconst REGEX_META = /(?<=[^\\s|])\\|(?=[^\\s|])|\\\\[dDwWsSbB]|\\.[*+]/;\n\n/**\n * The `//` line explaining a zero-match result, or undefined. Without it a\n * regex matched literally, or a glob that selected no files, reads exactly\n * like a genuine miss (issue #55). An `incomplete` scan (files skipped or\n * failed, scan stopped) already has its own line, which explains the zero\n * better, so it gets no hint.\n */\nexport function zeroMatchHint(p: {\n searchPattern: string;\n isRegex: boolean;\n pattern?: string | undefined;\n filesScanned: number;\n incomplete: boolean;\n}): string | undefined {\n if (p.incomplete) return undefined;\n if (p.filesScanned === 0) {\n return p.pattern === undefined\n ? '// no files were searched: nothing under path passed the hidden/ignored/maxDepth filters.'\n : `// no files were searched: nothing under path matched pattern '${p.pattern}' after the hidden/ignored/maxDepth filters. pattern is a file-name glob; the text to find goes in searchPattern.`;\n }\n if (!p.isRegex && REGEX_META.test(p.searchPattern)) {\n return '// searchPattern was matched as literal text but looks like a regex; pass isRegex=true to match it as one.';\n }\n return undefined;\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';\n\nimport { MAX_SEARCH_RESULTS } from './util.ts';\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\nconst SIZE_UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];\n\n/** Human-readable byte size for tree lines and summary text: `5 B`, `1 KiB`, `1.5 KiB`, `20 KiB`. */\nexport function formatByteSize(bytes: number): string {\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < SIZE_UNITS.length - 1) {\n value /= 1024;\n unit++;\n }\n let rounded = unit === 0 || value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;\n if (rounded >= 1024 && unit < SIZE_UNITS.length - 1) {\n rounded = 1;\n unit++;\n }\n return `${String(rounded)} ${SIZE_UNITS[unit]}`;\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): string {\n return pattern.length <= 40 ? pattern : `${pattern.slice(0, 40)}…`;\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, the\n * engine's stop state and the files it skipped live in the structured half,\n * which `defineTool` ships under `_meta` for a tool that authors its own text —\n * and no client renders that. These lines are the only place a caller learns\n * that more remains, that the scan was cut, or that files went unexamined (a\n * lone match in an oversized file otherwise reads as \"No matches\"). They are\n * independent: a scan can stop early with too few results to page, so a\n * truncation with no cursor must still say so. Prefixed `//` so none can be\n * 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 /** The call's own arguments; printed merged with the cursor so the hint is sendable as-is. */\n nextArgs?: Record<string, unknown> | undefined;\n stoppedReason?: string | undefined;\n skippedTooLarge?: number | undefined;\n skippedInaccessible?: number | 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({ ...p.nextArgs, 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) lines.push(stoppedEarlyLine(p.stoppedReason));\n const skipped = [\n p.skippedTooLarge ? `${formatCount(p.skippedTooLarge, 'file')} over the text size limit` : '',\n p.skippedInaccessible ? formatCount(p.skippedInaccessible, 'inaccessible file') : '',\n ].filter(Boolean);\n if (skipped.length > 0) {\n lines.push(`// skipped ${skipped.join(', ')}; results may be incomplete.`);\n }\n return lines.length > 0 ? `\\n\\n${lines.join('\\n')}` : '';\n}\n\n/** The `//` line saying a scan was cut short. */\nexport function stoppedEarlyLine(stoppedReason: string): string {\n const why = STOP_REASON_TEXT[stoppedReason] ?? `stopped (${stoppedReason})`;\n return `// scan stopped early: ${why}. That total is a floor, not the count. Narrow path or pattern for the rest.`;\n}\n\n// Regex syntax literal code rarely holds: unspaced alternation (`a|b`, not\n// `||` or a `string | null` union), class escapes, and `.*` / `.+`. Bare `(`,\n// `.`, `[` stay out — `foo(` and `a.b` are ordinary literal needles. A Windows\n// path like `src\\db` can still trip `\\d`; the hint only shows on zero\n// matches, so a stray one costs one line.\nconst REGEX_META = /(?<=[^\\s|])\\|(?=[^\\s|])|\\\\[dDwWsSbB]|\\.[*+]/;\n\n/**\n * The `//` line explaining a zero-match result, or undefined. Without it a\n * regex matched literally, or a glob that selected no files, reads exactly\n * like a genuine miss (issue #55). An `incomplete` scan (files skipped or\n * failed, scan stopped) already has its own line, which explains the zero\n * better, so it gets no hint.\n */\nexport function zeroMatchHint(p: {\n searchPattern: string;\n isRegex: boolean;\n pattern?: string | undefined;\n filesScanned: number;\n incomplete: boolean;\n}): string | undefined {\n if (p.incomplete) return undefined;\n if (p.filesScanned === 0) {\n return p.pattern === undefined\n ? '// no files were searched: nothing under path passed the hidden/ignored/maxDepth filters.'\n : `// no files were searched: nothing under path matched pattern '${p.pattern}' after the hidden/ignored/maxDepth filters. pattern is a file-name glob; the text to find goes in searchPattern.`;\n }\n if (!p.isRegex && REGEX_META.test(p.searchPattern)) {\n return '// searchPattern was matched as literal text but looks like a regex; pass isRegex=true to match it as one.';\n }\n return undefined;\n}\n"]}
|
package/dist/core/fs.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Stats } from 'node:fs';
|
|
2
2
|
import type { FileHandle } from 'node:fs/promises';
|
|
3
3
|
import { cp as fsCp, mkdir as fsMkdir, readlink as fsReadlink, rm as fsRm, rmdir as fsRmdir } from 'node:fs/promises';
|
|
4
|
+
import { type FileKind } from './mime.ts';
|
|
4
5
|
import type { EntryType as FileType } from './path-utils.ts';
|
|
5
6
|
import type { PathGuard } from './path.ts';
|
|
6
7
|
import type { ReadFileResult, ReadSpec } from './read.ts';
|
|
@@ -24,7 +25,7 @@ export declare class GuardedFileSystem {
|
|
|
24
25
|
validPath: string;
|
|
25
26
|
}>;
|
|
26
27
|
lstat(filePath: string, options?: {
|
|
27
|
-
signal?: AbortSignal;
|
|
28
|
+
signal?: AbortSignal | undefined;
|
|
28
29
|
}): Promise<{
|
|
29
30
|
stats: Stats;
|
|
30
31
|
validPath: string;
|
|
@@ -84,7 +85,10 @@ export declare class GuardedFileSystem {
|
|
|
84
85
|
}): Promise<{
|
|
85
86
|
content: Buffer;
|
|
86
87
|
mimeType: string;
|
|
88
|
+
kind: FileKind;
|
|
87
89
|
isBinary: boolean;
|
|
90
|
+
validPath: string;
|
|
91
|
+
modified: string;
|
|
88
92
|
}>;
|
|
89
93
|
open(filePath: string): Promise<FileHandle>;
|
|
90
94
|
statDetailed(filePath: string, options?: {
|
|
@@ -95,6 +99,17 @@ export declare class GuardedFileSystem {
|
|
|
95
99
|
stats: Stats;
|
|
96
100
|
}>;
|
|
97
101
|
hasChildrenUnchecked(dirPath: string): Promise<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Direct children of a directory, leaving out entries the guard treats as
|
|
104
|
+
* sensitive so the count never reveals what listings hide. Hidden and
|
|
105
|
+
* ignored entries count: this answers "is it empty?", not "what would list show?".
|
|
106
|
+
* A symlinked child counts only when list would show it. The path validation, open, opendir,
|
|
107
|
+
* each read and each link check race the signal, so a stalled mount ends the call at its
|
|
108
|
+
* deadline.
|
|
109
|
+
*/
|
|
110
|
+
countEntries(dirPath: string, options?: {
|
|
111
|
+
signal?: AbortSignal;
|
|
112
|
+
}): Promise<number>;
|
|
98
113
|
}
|
|
99
114
|
/**
|
|
100
115
|
* Returns whether `path` exists, treating "not found" as absent and logging any
|
package/dist/core/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/core/fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/core/fs.ts"],"names":[],"mappings":"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"}
|