@j0hanz/filesystem-mcp 2.8.1 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -26
- 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/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 +8 -0
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +84 -11
- 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 +159 -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 +6 -1
- package/dist/core/util.d.ts.map +1 -1
- package/dist/core/util.js +8 -1
- package/dist/core/util.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +18 -9
- 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 +138 -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 +114 -45
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-text.d.ts.map +1 -1
- package/dist/tools/replace-text.js +49 -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/schema.d.ts
CHANGED
|
@@ -25,18 +25,21 @@ export type SingleOrBatchShape<TExtra extends z.ZodRawShape> = TExtra & {
|
|
|
25
25
|
};
|
|
26
26
|
export declare function singleOrBatchPathsInput<TExtra extends z.ZodRawShape>(extra: TExtra): z.ZodObject<SingleOrBatchShape<TExtra>>;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* (`{ path?, paths?, files?[{path}] }`)
|
|
30
|
-
*
|
|
31
|
-
* superRefine), so they are checked in priority order.
|
|
28
|
+
* `ToolDef.mapPaths` for a {@link singleOrBatchPathsInput} shape
|
|
29
|
+
* (`{ path?, paths?, files?[{path}] }`). The schema's superRefine lets exactly
|
|
30
|
+
* one of the three through; each present one is mapped.
|
|
32
31
|
*/
|
|
33
|
-
export declare function
|
|
32
|
+
export declare function mapSingleOrBatchPaths<T extends {
|
|
34
33
|
path?: string | undefined;
|
|
35
34
|
paths?: string[] | undefined;
|
|
36
35
|
files?: readonly {
|
|
37
36
|
path: string;
|
|
38
37
|
}[] | undefined;
|
|
39
|
-
}):
|
|
38
|
+
}>(args: T, fn: (path: string) => string): T;
|
|
39
|
+
/** `ToolDef.mapPaths` for a tool whose one `path` is optional (omitted = the root). */
|
|
40
|
+
export declare function mapOptionalPath<T extends {
|
|
41
|
+
path?: string | undefined;
|
|
42
|
+
}>(args: T, fn: (path: string) => string): T;
|
|
40
43
|
export declare const CursorSchema: z.ZodOptional<z.ZodString>;
|
|
41
44
|
/**
|
|
42
45
|
* Wraps a tool's input schema so a stringified-then-spread payload (see
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAU5B,eAAO,MAAM,WAAW,UAEe,CAAC;AAQxC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,CAMzD;AAyBD,eAAO,MAAM,YAAY,aAS8C,CAAC;AAExE,eAAO,MAAM,YAAY,4BAA0B,CAAC;AAEpD,eAAO,MAAM,eAAe,aAYsC,CAAC;AAEnE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE;IACL,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,EACD,GAAG,EAAE,CAAC,CAAC,aAAa,GACnB,IAAI,CAyCN;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAEnF;AAED,eAAO,MAAM,aAAa,4BAAgE,CAAC;AAC3F,eAAO,MAAM,cAAc,4BAE1B,CAAC;AACF,eAAO,MAAM,QAAQ,4BAKkE,CAAC;AAIxF,MAAM,MAAM,kBAAkB,CAAC,MAAM,SAAS,CAAC,CAAC,WAAW,IAAI,MAAM,GAAG;IACtE,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO,YAAY,CAAC,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC;CACvD,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,MAAM,SAAS,CAAC,CAAC,WAAW,EAClE,KAAK,EAAE,MAAM,GACZ,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAyCzC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,SAAS;IACR,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;CACjD,EACD,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,CAAC,CAS1C;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EACrE,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC3B,CAAC,CAEH;AAQD,eAAO,MAAM,YAAY,4BAKtB,CAAC;AAqBJ;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAoBxF"}
|
package/dist/core/schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from 'zod/v4';
|
|
2
|
-
import { isSafeGlobSyntax, MAX_BRACE_ALTERNATIVES, MAX_DOTDOT_CHECK_CHARS } from "./glob.js";
|
|
2
|
+
import { isSafeGlobSyntax, MAX_BRACE_ALTERNATIVES, MAX_DOTDOT_CHECK_CHARS, MAX_SEGMENT_WILDCARDS, } from "./glob.js";
|
|
3
3
|
import { MAX_SEARCH_DEPTH } from "./util.js";
|
|
4
4
|
export const PositiveInt = z
|
|
5
5
|
.int({ message: 'Must be integer' })
|
|
@@ -51,7 +51,7 @@ export const SafeGlobPattern = z
|
|
|
51
51
|
.max(1000, { message: 'Max 1000 chars' })
|
|
52
52
|
.superRefine(refineSafeText('Pattern', (val) => isSafeGlobSyntax(val)
|
|
53
53
|
? undefined
|
|
54
|
-
: `Invalid glob or unsafe path (absolute/.. forbidden, at most ${String(MAX_BRACE_ALTERNATIVES)} brace alternatives
|
|
54
|
+
: `Invalid glob or unsafe path (absolute/.. forbidden, at most ${String(MAX_BRACE_ALTERNATIVES)} brace alternatives, ${String(MAX_DOTDOT_CHECK_CHARS)} traversal-check characters, and ${String(MAX_SEGMENT_WILDCARDS)} * runs and extglob groups together per path segment; no |, * or nested group inside *(…) or +(…))`))
|
|
55
55
|
.describe('Relative glob pattern under the search root (e.g. "**/*.ts", "src/**/*.js").')
|
|
56
56
|
.meta({ examples: ['**/*.ts', 'src/**/*.js', '**/*.{ts,tsx}'] });
|
|
57
57
|
export function validateReadRange(value, ctx) {
|
|
@@ -137,26 +137,29 @@ export function singleOrBatchPathsInput(extra) {
|
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
});
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
});
|
|
140
|
+
// No top-level oneOf on the wire: OpenAI and Anthropic reject top-level
|
|
141
|
+
// oneOf/anyOf/allOf in function parameters. The superRefine enforces
|
|
142
|
+
// "exactly one of path/paths" at call time.
|
|
143
|
+
return base;
|
|
145
144
|
}
|
|
146
145
|
/**
|
|
147
|
-
*
|
|
148
|
-
* (`{ path?, paths?, files?[{path}] }`)
|
|
149
|
-
*
|
|
150
|
-
* superRefine), so they are checked in priority order.
|
|
146
|
+
* `ToolDef.mapPaths` for a {@link singleOrBatchPathsInput} shape
|
|
147
|
+
* (`{ path?, paths?, files?[{path}] }`). The schema's superRefine lets exactly
|
|
148
|
+
* one of the three through; each present one is mapped.
|
|
151
149
|
*/
|
|
152
|
-
export function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
150
|
+
export function mapSingleOrBatchPaths(args, fn) {
|
|
151
|
+
return {
|
|
152
|
+
...args,
|
|
153
|
+
...(args.path !== undefined ? { path: fn(args.path) } : {}),
|
|
154
|
+
...(args.paths !== undefined ? { paths: args.paths.map(fn) } : {}),
|
|
155
|
+
...(args.files !== undefined
|
|
156
|
+
? { files: args.files.map((file) => ({ ...file, path: fn(file.path) })) }
|
|
157
|
+
: {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** `ToolDef.mapPaths` for a tool whose one `path` is optional (omitted = the root). */
|
|
161
|
+
export function mapOptionalPath(args, fn) {
|
|
162
|
+
return args.path === undefined ? args : { ...args, path: fn(args.path) };
|
|
160
163
|
}
|
|
161
164
|
// ponytail: charset regex, not z.base64url() — the SDK's AJV warns on the
|
|
162
165
|
// unknown `base64url` format; the server's own decode (cursor.ts) is the real
|
package/dist/core/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { isSafeGlobSyntax, MAX_BRACE_ALTERNATIVES, MAX_DOTDOT_CHECK_CHARS } from './glob.ts';\nimport { MAX_SEARCH_DEPTH } from './util.ts';\n\nexport const PositiveInt = z\n .int({ message: 'Must be integer' })\n .positive({ message: 'Must be > 0' });\n\nconst MAX_PATH_LENGTH = 4096;\n\nconst LINE_BREAK_RE = /[\\n\\r]/;\n\nconst isBlank = (val: string): boolean => val.trim().length === 0;\n\n/**\n * Text that must carry non-blank content. `min(1)` stays so `minLength: 1`\n * still publishes on the wire; it aborts so an empty value reports one\n * message, not the same one twice.\n */\nexport function nonBlankString(label: string): z.ZodString {\n const message = `${label} cannot be empty or whitespace-only`;\n return z\n .string()\n .min(1, { message, abort: true })\n .refine((val) => !isBlank(val), { message });\n}\n\nfunction refineSafeText(\n label: string,\n specific: (val: string) => string | undefined,\n): (val: string, ctx: z.RefinementCtx) => void {\n return (val, ctx) => {\n if (val.length === 0) return;\n const issue = isBlank(val)\n ? `${label} cannot be empty or whitespace-only`\n : val.includes('\\0')\n ? `${label} cannot contain null bytes`\n : (specific(val) ??\n (LINE_BREAK_RE.test(val) ? `${label} cannot contain line breaks` : undefined));\n if (issue !== undefined) {\n ctx.addIssue({ code: 'custom', message: issue, fatal: true });\n }\n };\n}\n\n// Only a whole `..` segment is traversal. `..` inside a segment\n// (`[...slug]`, `v1..2.md`) is a plain file name; PathGuard re-checks\n// containment on the resolved path regardless.\nconst hasTraversalSegment = (val: string): boolean => val.split(/[\\\\/]/u).includes('..');\n\nexport const RequiredPath = z\n .string()\n .min(1, { message: 'Path required' })\n .max(MAX_PATH_LENGTH, { message: `Path too long (max ${MAX_PATH_LENGTH} chars)` })\n .superRefine(\n refineSafeText('Path', (val) =>\n hasTraversalSegment(val) ? 'Directory traversal sequences (\"..\") are forbidden' : undefined,\n ),\n )\n .describe('File or directory path inside an allowed workspace root.');\n\nexport const OptionalPath = RequiredPath.optional();\n\nexport const SafeGlobPattern = z\n .string()\n .min(1, { message: 'Pattern required' })\n .max(1000, { message: 'Max 1000 chars' })\n .superRefine(\n refineSafeText('Pattern', (val) =>\n isSafeGlobSyntax(val)\n ? undefined\n : `Invalid glob or unsafe path (absolute/.. forbidden, at most ${String(MAX_BRACE_ALTERNATIVES)} brace alternatives and ${String(MAX_DOTDOT_CHECK_CHARS)} traversal-check characters)`,\n ),\n )\n .describe('Relative glob pattern under the search root (e.g. \"**/*.ts\", \"src/**/*.js\").')\n .meta({ examples: ['**/*.ts', 'src/**/*.js', '**/*.{ts,tsx}'] });\n\nexport function validateReadRange(\n value: {\n head?: number | undefined;\n tail?: number | undefined;\n startLine?: number | undefined;\n endLine?: number | undefined;\n },\n ctx: z.RefinementCtx,\n): void {\n const hasHead = value.head !== undefined;\n const hasTail = value.tail !== undefined;\n const hasStart = value.startLine !== undefined;\n const hasEnd = value.endLine !== undefined;\n\n if (hasHead && (hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['head'],\n message:\n \"Cannot use 'head' with 'startLine'/'endLine'. Use either 'head' alone or 'startLine'/'endLine' together.\",\n input: value,\n });\n }\n if (hasTail && (hasHead || hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['tail'],\n message:\n \"Cannot use 'tail' with 'head'/'startLine'/'endLine'. Use 'tail' alone, or 'startLine'/'endLine' without 'tail'.\",\n input: value,\n });\n }\n if (hasEnd && !hasStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' requires 'startLine' to be set. Provide both together.\",\n input: value,\n });\n }\n const effectiveStart = value.startLine ?? 1;\n if (value.endLine !== undefined && value.endLine < effectiveStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' must be >= 'startLine'\",\n input: value,\n });\n }\n}\n\nexport function defaultFalseBoolean(description: string): z.ZodDefault<z.ZodBoolean> {\n return z.boolean().default(false).describe(description);\n}\n\nexport const IncludeHidden = defaultFalseBoolean('Include hidden items (starting with .)');\nexport const IncludeIgnored = defaultFalseBoolean(\n 'Include ignored items (node_modules, .git, etc).',\n);\nexport const MaxDepth = z\n .uint32()\n .min(0)\n .max(MAX_SEARCH_DEPTH)\n .optional()\n .describe('Max directory depth to scan; 0 = base directory only, omit for unlimited');\n\nconst DEFAULT_MAX_BATCH = 1000;\n\nexport type SingleOrBatchShape<TExtra extends z.ZodRawShape> = TExtra & {\n path: z.ZodOptional<typeof RequiredPath>;\n paths: z.ZodOptional<z.ZodArray<typeof RequiredPath>>;\n};\n\nexport function singleOrBatchPathsInput<TExtra extends z.ZodRawShape>(\n extra: TExtra,\n): z.ZodObject<SingleOrBatchShape<TExtra>> {\n const shape: z.ZodRawShape = {\n ...extra,\n path: RequiredPath.optional().describe('Single file path; mutually exclusive with paths'),\n paths: z\n .array(RequiredPath)\n .min(1)\n .max(DEFAULT_MAX_BATCH)\n .optional()\n .describe(\n `Array of file paths for batch mode (max ${String(DEFAULT_MAX_BATCH)}); mutually exclusive with path`,\n ),\n };\n\n const base = z.strictObject(shape).superRefine((value, ctx) => {\n const hasPath = value['path'] !== undefined;\n const hasPaths = value['paths'] !== undefined;\n\n if (!hasPath && !hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['path'],\n message: \"Either 'path' or 'paths' must be provided\",\n input: value,\n });\n return;\n }\n if (hasPath && hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['paths'],\n message: \"Cannot use both 'path' and 'paths'\",\n input: value,\n });\n }\n });\n\n // Mirror the superRefine above on the wire: exactly one input mode. `{}` and\n // `path`+`paths` both fail this oneOf, matching the runtime rule.\n return base.meta({\n oneOf: [{ required: ['path'] }, { required: ['paths'] }],\n }) as z.ZodObject<SingleOrBatchShape<TExtra>>;\n}\n\n/**\n * Extract the filesystem paths from a {@link singleOrBatchPathsInput} shape\n * (`{ path?, paths?, files?[{path}] }`) for the executor's access-grant\n * pre-check. Exactly one of the three is set (enforced by the schema's\n * superRefine), so they are checked in priority order.\n */\nexport function singleOrBatchAccessPaths(args: {\n path?: string | undefined;\n paths?: string[] | undefined;\n files?: readonly { path: string }[] | undefined;\n}): string[] {\n if (args.path) return [args.path];\n if (args.paths) return [...args.paths];\n if (args.files) return args.files.map((f) => f.path);\n return [];\n}\n\n// ponytail: charset regex, not z.base64url() — the SDK's AJV warns on the\n// unknown `base64url` format; the server's own decode (cursor.ts) is the real\n// validation, so loosening the client-facing schema to the alphabet is safe.\nconst base64urlCursor = z.string().regex(/^[A-Za-z0-9_-]+$/);\n\n// Ships once per paginated tool — wording is budgeted (TOOL-SURFACE-002).\nexport const CursorSchema = base64urlCursor\n .optional()\n .describe(\n 'Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot ' +\n 'taken on the first call; it expires after ~60s — re-request without a cursor if rejected.',\n );\n\n/**\n * Reassembles tool arguments that a client JSON-serialized and then spread\n * into an object: `{ path }` arrives as `{ \"0\": \"{\", \"1\": \"\\\"\", … }`, one key\n * per UTF-16 code unit. Returns `undefined` for anything else. Integer keys\n * enumerate first in ascending order, so matching each key to its position\n * also proves the keys are exactly `\"0\"…\"n-1\"` with nothing else alongside.\n */\nfunction joinSplitString(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const entries = Object.entries(value);\n if (entries.length === 0) return undefined;\n let text = '';\n for (const [index, [key, char]] of entries.entries()) {\n if (key !== String(index) || typeof char !== 'string' || char.length !== 1) return undefined;\n text += char;\n }\n return text;\n}\n\n/**\n * Wraps a tool's input schema so a stringified-then-spread payload (see\n * {@link joinSplitString}) is parsed back into the object it encodes, and one\n * that does not encode an object fails with a message naming the cause rather\n * than `Unrecognized keys: \"0\", \"1\", …`. The published JSON Schema is the\n * wrapped schema's own: a preprocess step converts to its output side.\n */\nexport function withSplitArgsRecovery<T extends z.ZodType>(schema: T): z.ZodPreprocess<T> {\n return z.preprocess((value, ctx) => {\n const text = joinSplitString(value);\n if (text === undefined) return value;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = undefined;\n }\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) return parsed;\n ctx.addIssue({\n code: 'custom',\n message:\n `arguments must be a JSON object, but arrived as a ${String(text.length)}-character ` +\n 'string split into indexed keys (the client serialized the payload); send the arguments as an object',\n input: value,\n });\n return z.NEVER;\n }, schema);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport {\n isSafeGlobSyntax,\n MAX_BRACE_ALTERNATIVES,\n MAX_DOTDOT_CHECK_CHARS,\n MAX_SEGMENT_WILDCARDS,\n} from './glob.ts';\nimport { MAX_SEARCH_DEPTH } from './util.ts';\n\nexport const PositiveInt = z\n .int({ message: 'Must be integer' })\n .positive({ message: 'Must be > 0' });\n\nconst MAX_PATH_LENGTH = 4096;\n\nconst LINE_BREAK_RE = /[\\n\\r]/;\n\nconst isBlank = (val: string): boolean => val.trim().length === 0;\n\n/**\n * Text that must carry non-blank content. `min(1)` stays so `minLength: 1`\n * still publishes on the wire; it aborts so an empty value reports one\n * message, not the same one twice.\n */\nexport function nonBlankString(label: string): z.ZodString {\n const message = `${label} cannot be empty or whitespace-only`;\n return z\n .string()\n .min(1, { message, abort: true })\n .refine((val) => !isBlank(val), { message });\n}\n\nfunction refineSafeText(\n label: string,\n specific: (val: string) => string | undefined,\n): (val: string, ctx: z.RefinementCtx) => void {\n return (val, ctx) => {\n if (val.length === 0) return;\n const issue = isBlank(val)\n ? `${label} cannot be empty or whitespace-only`\n : val.includes('\\0')\n ? `${label} cannot contain null bytes`\n : (specific(val) ??\n (LINE_BREAK_RE.test(val) ? `${label} cannot contain line breaks` : undefined));\n if (issue !== undefined) {\n ctx.addIssue({ code: 'custom', message: issue, fatal: true });\n }\n };\n}\n\n// Only a whole `..` segment is traversal. `..` inside a segment\n// (`[...slug]`, `v1..2.md`) is a plain file name; PathGuard re-checks\n// containment on the resolved path regardless.\nconst hasTraversalSegment = (val: string): boolean => val.split(/[\\\\/]/u).includes('..');\n\nexport const RequiredPath = z\n .string()\n .min(1, { message: 'Path required' })\n .max(MAX_PATH_LENGTH, { message: `Path too long (max ${MAX_PATH_LENGTH} chars)` })\n .superRefine(\n refineSafeText('Path', (val) =>\n hasTraversalSegment(val) ? 'Directory traversal sequences (\"..\") are forbidden' : undefined,\n ),\n )\n .describe('File or directory path inside an allowed workspace root.');\n\nexport const OptionalPath = RequiredPath.optional();\n\nexport const SafeGlobPattern = z\n .string()\n .min(1, { message: 'Pattern required' })\n .max(1000, { message: 'Max 1000 chars' })\n .superRefine(\n refineSafeText('Pattern', (val) =>\n isSafeGlobSyntax(val)\n ? undefined\n : `Invalid glob or unsafe path (absolute/.. forbidden, at most ${String(MAX_BRACE_ALTERNATIVES)} brace alternatives, ${String(MAX_DOTDOT_CHECK_CHARS)} traversal-check characters, and ${String(MAX_SEGMENT_WILDCARDS)} * runs and extglob groups together per path segment; no |, * or nested group inside *(…) or +(…))`,\n ),\n )\n .describe('Relative glob pattern under the search root (e.g. \"**/*.ts\", \"src/**/*.js\").')\n .meta({ examples: ['**/*.ts', 'src/**/*.js', '**/*.{ts,tsx}'] });\n\nexport function validateReadRange(\n value: {\n head?: number | undefined;\n tail?: number | undefined;\n startLine?: number | undefined;\n endLine?: number | undefined;\n },\n ctx: z.RefinementCtx,\n): void {\n const hasHead = value.head !== undefined;\n const hasTail = value.tail !== undefined;\n const hasStart = value.startLine !== undefined;\n const hasEnd = value.endLine !== undefined;\n\n if (hasHead && (hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['head'],\n message:\n \"Cannot use 'head' with 'startLine'/'endLine'. Use either 'head' alone or 'startLine'/'endLine' together.\",\n input: value,\n });\n }\n if (hasTail && (hasHead || hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['tail'],\n message:\n \"Cannot use 'tail' with 'head'/'startLine'/'endLine'. Use 'tail' alone, or 'startLine'/'endLine' without 'tail'.\",\n input: value,\n });\n }\n if (hasEnd && !hasStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' requires 'startLine' to be set. Provide both together.\",\n input: value,\n });\n }\n const effectiveStart = value.startLine ?? 1;\n if (value.endLine !== undefined && value.endLine < effectiveStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' must be >= 'startLine'\",\n input: value,\n });\n }\n}\n\nexport function defaultFalseBoolean(description: string): z.ZodDefault<z.ZodBoolean> {\n return z.boolean().default(false).describe(description);\n}\n\nexport const IncludeHidden = defaultFalseBoolean('Include hidden items (starting with .)');\nexport const IncludeIgnored = defaultFalseBoolean(\n 'Include ignored items (node_modules, .git, etc).',\n);\nexport const MaxDepth = z\n .uint32()\n .min(0)\n .max(MAX_SEARCH_DEPTH)\n .optional()\n .describe('Max directory depth to scan; 0 = base directory only, omit for unlimited');\n\nconst DEFAULT_MAX_BATCH = 1000;\n\nexport type SingleOrBatchShape<TExtra extends z.ZodRawShape> = TExtra & {\n path: z.ZodOptional<typeof RequiredPath>;\n paths: z.ZodOptional<z.ZodArray<typeof RequiredPath>>;\n};\n\nexport function singleOrBatchPathsInput<TExtra extends z.ZodRawShape>(\n extra: TExtra,\n): z.ZodObject<SingleOrBatchShape<TExtra>> {\n const shape: z.ZodRawShape = {\n ...extra,\n path: RequiredPath.optional().describe('Single file path; mutually exclusive with paths'),\n paths: z\n .array(RequiredPath)\n .min(1)\n .max(DEFAULT_MAX_BATCH)\n .optional()\n .describe(\n `Array of file paths for batch mode (max ${String(DEFAULT_MAX_BATCH)}); mutually exclusive with path`,\n ),\n };\n\n const base = z.strictObject(shape).superRefine((value, ctx) => {\n const hasPath = value['path'] !== undefined;\n const hasPaths = value['paths'] !== undefined;\n\n if (!hasPath && !hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['path'],\n message: \"Either 'path' or 'paths' must be provided\",\n input: value,\n });\n return;\n }\n if (hasPath && hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['paths'],\n message: \"Cannot use both 'path' and 'paths'\",\n input: value,\n });\n }\n });\n\n // No top-level oneOf on the wire: OpenAI and Anthropic reject top-level\n // oneOf/anyOf/allOf in function parameters. The superRefine enforces\n // \"exactly one of path/paths\" at call time.\n return base as z.ZodObject<SingleOrBatchShape<TExtra>>;\n}\n\n/**\n * `ToolDef.mapPaths` for a {@link singleOrBatchPathsInput} shape\n * (`{ path?, paths?, files?[{path}] }`). The schema's superRefine lets exactly\n * one of the three through; each present one is mapped.\n */\nexport function mapSingleOrBatchPaths<\n T extends {\n path?: string | undefined;\n paths?: string[] | undefined;\n files?: readonly { path: string }[] | undefined;\n },\n>(args: T, fn: (path: string) => string): T {\n return {\n ...args,\n ...(args.path !== undefined ? { path: fn(args.path) } : {}),\n ...(args.paths !== undefined ? { paths: args.paths.map(fn) } : {}),\n ...(args.files !== undefined\n ? { files: args.files.map((file) => ({ ...file, path: fn(file.path) })) }\n : {}),\n };\n}\n\n/** `ToolDef.mapPaths` for a tool whose one `path` is optional (omitted = the root). */\nexport function mapOptionalPath<T extends { path?: string | undefined }>(\n args: T,\n fn: (path: string) => string,\n): T {\n return args.path === undefined ? args : { ...args, path: fn(args.path) };\n}\n\n// ponytail: charset regex, not z.base64url() — the SDK's AJV warns on the\n// unknown `base64url` format; the server's own decode (cursor.ts) is the real\n// validation, so loosening the client-facing schema to the alphabet is safe.\nconst base64urlCursor = z.string().regex(/^[A-Za-z0-9_-]+$/);\n\n// Ships once per paginated tool — wording is budgeted (TOOL-SURFACE-002).\nexport const CursorSchema = base64urlCursor\n .optional()\n .describe(\n 'Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot ' +\n 'taken on the first call; it expires after ~60s — re-request without a cursor if rejected.',\n );\n\n/**\n * Reassembles tool arguments that a client JSON-serialized and then spread\n * into an object: `{ path }` arrives as `{ \"0\": \"{\", \"1\": \"\\\"\", … }`, one key\n * per UTF-16 code unit. Returns `undefined` for anything else. Integer keys\n * enumerate first in ascending order, so matching each key to its position\n * also proves the keys are exactly `\"0\"…\"n-1\"` with nothing else alongside.\n */\nfunction joinSplitString(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const entries = Object.entries(value);\n if (entries.length === 0) return undefined;\n let text = '';\n for (const [index, [key, char]] of entries.entries()) {\n if (key !== String(index) || typeof char !== 'string' || char.length !== 1) return undefined;\n text += char;\n }\n return text;\n}\n\n/**\n * Wraps a tool's input schema so a stringified-then-spread payload (see\n * {@link joinSplitString}) is parsed back into the object it encodes, and one\n * that does not encode an object fails with a message naming the cause rather\n * than `Unrecognized keys: \"0\", \"1\", …`. The published JSON Schema is the\n * wrapped schema's own: a preprocess step converts to its output side.\n */\nexport function withSplitArgsRecovery<T extends z.ZodType>(schema: T): z.ZodPreprocess<T> {\n return z.preprocess((value, ctx) => {\n const text = joinSplitString(value);\n if (text === undefined) return value;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = undefined;\n }\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) return parsed;\n ctx.addIssue({\n code: 'custom',\n message:\n `arguments must be a JSON object, but arrived as a ${String(text.length)}-character ` +\n 'string split into indexed keys (the client serialized the payload); send the arguments as an object',\n input: value,\n });\n return z.NEVER;\n }, schema);\n}\n"]}
|
package/dist/core/search.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RE2 } from '@adguard/re2-wasm';
|
|
2
|
+
import { GuardedFileSystem } from './fs.ts';
|
|
2
3
|
import type { PathGuard } from './path.ts';
|
|
3
4
|
interface SearchResult {
|
|
4
5
|
file: string;
|
|
@@ -155,5 +156,10 @@ export declare function searchFiles(directory: string, pattern: string, options:
|
|
|
155
156
|
stoppedReason?: 'maxResults' | 'timeout';
|
|
156
157
|
};
|
|
157
158
|
}>;
|
|
159
|
+
/** Explicit files bypass glob filtering; their parent is the directory walked. */
|
|
160
|
+
export declare function resolveSearchScope(path: string | undefined, fs: GuardedFileSystem, signal?: AbortSignal): Promise<{
|
|
161
|
+
basePath: string;
|
|
162
|
+
explicitFile?: string;
|
|
163
|
+
}>;
|
|
158
164
|
export {};
|
|
159
165
|
//# sourceMappingURL=search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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"}
|
package/dist/core/search.js
CHANGED
|
@@ -50,9 +50,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
50
50
|
var e = new Error(message);
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
|
-
import { basename } from 'node:path';
|
|
53
|
+
import { basename, dirname } from 'node:path';
|
|
54
54
|
import { RE2 } from '@adguard/re2-wasm';
|
|
55
55
|
import { processEntriesConcurrently, resolveStopReason } from "./concurrency.js";
|
|
56
|
+
import { ErrorCode, FsError, isFsError } from "./errors.js";
|
|
56
57
|
import { GuardedFileSystem } from "./fs.js";
|
|
57
58
|
import { globEntries } from "./glob.js";
|
|
58
59
|
import { escapeRegExp, getMaxTextFileSize, MIB, PARALLEL_CONCURRENCY } from "./util.js";
|
|
@@ -88,6 +89,60 @@ export function createYielder(signal) {
|
|
|
88
89
|
* aborts the module. 4 MiB leaves room for the copy and the pattern set.
|
|
89
90
|
*/
|
|
90
91
|
export const RE2_MAX_INPUT_BYTES = 4 * MIB;
|
|
92
|
+
/** The chunk `FileHandle#readFile` reads in, and checks its signal between. */
|
|
93
|
+
const READ_CHUNK_BYTES = 512 * 1024;
|
|
94
|
+
/** The only non-ASCII characters RE2's case folding maps onto ASCII letters. */
|
|
95
|
+
const KELVIN_SIGN_UTF8 = Buffer.from('\u212A', 'utf8'); // folds onto k
|
|
96
|
+
const LONG_S_UTF8 = Buffer.from('\u017F', 'utf8'); // folds onto s
|
|
97
|
+
const ASCII_UPPER_RE = /[A-Z]+/gu;
|
|
98
|
+
/** Non-overlapping occurrences, left to right, as a global RE2 exec walks them. */
|
|
99
|
+
function countOccurrences(line, needle) {
|
|
100
|
+
const column = line.indexOf(needle);
|
|
101
|
+
if (column === -1)
|
|
102
|
+
return undefined;
|
|
103
|
+
let count = 0;
|
|
104
|
+
let at = column;
|
|
105
|
+
while (at !== -1 && count < MAX_MATCHES_PER_LINE) {
|
|
106
|
+
count++;
|
|
107
|
+
at = line.indexOf(needle, at + needle.length);
|
|
108
|
+
}
|
|
109
|
+
return { count, column };
|
|
110
|
+
}
|
|
111
|
+
/** `undefined` when RE2 must judge the needle: these shortcuts are exact only for the needles they take. */
|
|
112
|
+
function createLiteralMatcher(needle, caseSensitive) {
|
|
113
|
+
// The file is decoded with U+FFFD for every invalid byte, and a lone
|
|
114
|
+
// surrogate reaches RE2 as U+FFFD, so a needle holding either can match
|
|
115
|
+
// decoded text that the raw bytes, or indexOf, do not.
|
|
116
|
+
if (needle.includes('\uFFFD') || Buffer.from(needle, 'utf8').toString('utf8') !== needle) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
if (caseSensitive) {
|
|
120
|
+
const bytes = Buffer.from(needle, 'utf8');
|
|
121
|
+
return {
|
|
122
|
+
needsRe2: () => false,
|
|
123
|
+
mayMatch: (buffer) => buffer.includes(bytes),
|
|
124
|
+
matchLine: (line) => countOccurrences(line, needle),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Every non-ASCII character takes more than one UTF-8 byte per UTF-16 unit.
|
|
128
|
+
if (Buffer.byteLength(needle, 'utf8') !== needle.length)
|
|
129
|
+
return undefined;
|
|
130
|
+
const lower = needle.toLowerCase();
|
|
131
|
+
const folds = [
|
|
132
|
+
...(lower.includes('k') ? [KELVIN_SIGN_UTF8] : []),
|
|
133
|
+
...(lower.includes('s') ? [LONG_S_UTF8] : []),
|
|
134
|
+
];
|
|
135
|
+
return {
|
|
136
|
+
needsRe2: (buffer) => folds.some((fold) => buffer.includes(fold)),
|
|
137
|
+
// Latin-1 decoding keeps one char per byte, and lowercasing it maps A-Z
|
|
138
|
+
// onto a-z and every other byte onto a non-ASCII char, so an ASCII needle
|
|
139
|
+
// is found exactly where the bytes spell it in either case.
|
|
140
|
+
mayMatch: (buffer) => buffer.toString('latin1').toLowerCase().includes(lower),
|
|
141
|
+
// ASCII letters only: String#toLowerCase turns U+0130 into two code units,
|
|
142
|
+
// which shifts every later column and spells an `i` RE2 does not match.
|
|
143
|
+
matchLine: (line) => countOccurrences(line.replace(ASCII_UPPER_RE, (upper) => upper.toLowerCase()), lower),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
91
146
|
const BUFFER_ANCHOR_RE = /\\[AzZ]/u;
|
|
92
147
|
/** `(?i)`, `(?-m)`, `(?s:…)`: inline flags can undo the `m` the prefilter relies on. */
|
|
93
148
|
const INLINE_FLAGS_RE = /\(\?[a-zA-Z-]+[:)]/u;
|
|
@@ -215,10 +270,34 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
215
270
|
// re-checks the descriptor's real path (H4), so these bytes are the
|
|
216
271
|
// validated file's, not whatever the name points at by now.
|
|
217
272
|
const handle = __addDisposableResource(env_1, await scanCtx.fs.open(entryPath), true);
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
273
|
+
const { size } = await handle.stat();
|
|
274
|
+
if (size > scanCtx.maxFileSize)
|
|
220
275
|
return { kind: 'tooLarge' };
|
|
221
|
-
|
|
276
|
+
// Read the size fstat reported: handle.readFile would fstat again and then
|
|
277
|
+
// read once more to find EOF, two extra round trips on every file a walk
|
|
278
|
+
// touches. A file that grows meanwhile is searched as it was at the fstat.
|
|
279
|
+
// A size of 0 is also what procfs and other virtual files report while
|
|
280
|
+
// holding text, so those still read to EOF.
|
|
281
|
+
if (size === 0) {
|
|
282
|
+
buffer = await handle.readFile({ signal: scanCtx.signal });
|
|
283
|
+
if (buffer.length > scanCtx.maxFileSize)
|
|
284
|
+
return { kind: 'tooLarge' };
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
buffer = Buffer.allocUnsafe(size);
|
|
288
|
+
let filled = 0;
|
|
289
|
+
while (filled < size) {
|
|
290
|
+
// In readFile's own chunks, with its check of the signal before each,
|
|
291
|
+
// so a deadline on a slow filesystem lands as soon as it did there.
|
|
292
|
+
scanCtx.signal?.throwIfAborted();
|
|
293
|
+
const length = Math.min(size - filled, READ_CHUNK_BYTES);
|
|
294
|
+
const { bytesRead } = await handle.read(buffer, filled, length, filled);
|
|
295
|
+
if (bytesRead === 0)
|
|
296
|
+
break;
|
|
297
|
+
filled += bytesRead;
|
|
298
|
+
}
|
|
299
|
+
buffer = buffer.subarray(0, filled);
|
|
300
|
+
}
|
|
222
301
|
}
|
|
223
302
|
catch (e_1) {
|
|
224
303
|
env_1.error = e_1;
|
|
@@ -233,6 +312,10 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
233
312
|
catch (error) {
|
|
234
313
|
if (scanCtx.signal?.aborted)
|
|
235
314
|
throw error;
|
|
315
|
+
if (scanCtx.fs.pathGuard.isSensitive(entryPath) ||
|
|
316
|
+
(isFsError(error) && error.message.startsWith('Sensitive file'))) {
|
|
317
|
+
return { kind: 'sensitive' };
|
|
318
|
+
}
|
|
236
319
|
return { kind: 'inaccessible' };
|
|
237
320
|
}
|
|
238
321
|
// A NUL byte marks a binary file, as it does for `read` and `grep -I`.
|
|
@@ -240,8 +323,11 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
240
323
|
// bytes a pattern could not have matched anyway.
|
|
241
324
|
if (buffer.includes(0))
|
|
242
325
|
return { kind: 'binary' };
|
|
326
|
+
const literal = scanCtx.literal?.needsRe2(buffer) ? undefined : scanCtx.literal;
|
|
327
|
+
if (literal && !literal.mayMatch(buffer))
|
|
328
|
+
return { kind: 'scanned', matches: [] };
|
|
243
329
|
const content = buffer.toString('utf-8');
|
|
244
|
-
if (canPrefilter(scanCtx.source, buffer)) {
|
|
330
|
+
if (!literal && canPrefilter(scanCtx.source, buffer)) {
|
|
245
331
|
scanCtx.prefilter.lastIndex = 0;
|
|
246
332
|
if (scanCtx.prefilter.exec(content) === null)
|
|
247
333
|
return { kind: 'scanned', matches: [] };
|
|
@@ -253,8 +339,9 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
253
339
|
const lineCount = content.length === 0 ? 0 : content.endsWith('\n') ? lines.length - 1 : lines.length;
|
|
254
340
|
// A single line past RE2_MAX_INPUT_BYTES OOMs the wasm module for the rest of
|
|
255
341
|
// the process; report the file tooLarge instead. Only a buffer over the bound
|
|
256
|
-
// can hold such a line, so ordinary files skip the per-line measure
|
|
257
|
-
|
|
342
|
+
// can hold such a line, so ordinary files skip the per-line measure, and a
|
|
343
|
+
// literal match never hands a line to RE2.
|
|
344
|
+
const checkLineSize = !literal && buffer.length > RE2_MAX_INPUT_BYTES;
|
|
258
345
|
const matches = [];
|
|
259
346
|
const tick = createYielder(scanCtx.signal);
|
|
260
347
|
for (let i = 0; i < lineCount; i++) {
|
|
@@ -269,7 +356,9 @@ async function scanFile(entryPath, scanCtx) {
|
|
|
269
356
|
break;
|
|
270
357
|
// One scan per line: the helper resets lastIndex itself, so it
|
|
271
358
|
// doubles as the "does this line match" test.
|
|
272
|
-
const found =
|
|
359
|
+
const found = literal
|
|
360
|
+
? literal.matchLine(line)
|
|
361
|
+
: await findLineMatches(scanCtx.regex, line, tick);
|
|
273
362
|
if (found) {
|
|
274
363
|
matches.push({
|
|
275
364
|
file: entryPath,
|
|
@@ -341,6 +430,9 @@ export async function searchContent(directory, pattern, options, pathGuard) {
|
|
|
341
430
|
maxResults,
|
|
342
431
|
regex,
|
|
343
432
|
prefilter,
|
|
433
|
+
literal: options.isRegex
|
|
434
|
+
? undefined
|
|
435
|
+
: createLiteralMatcher(pattern, Boolean(options.caseSensitive)),
|
|
344
436
|
source,
|
|
345
437
|
context,
|
|
346
438
|
};
|
|
@@ -369,6 +461,8 @@ export async function searchContent(directory, pattern, options, pathGuard) {
|
|
|
369
461
|
if (!scan)
|
|
370
462
|
continue;
|
|
371
463
|
switch (scan.kind) {
|
|
464
|
+
case 'sensitive':
|
|
465
|
+
break;
|
|
372
466
|
case 'inaccessible':
|
|
373
467
|
skippedInaccessible++;
|
|
374
468
|
break;
|
|
@@ -442,34 +536,47 @@ export async function searchFiles(directory, pattern, options, pathGuard) {
|
|
|
442
536
|
const results = [];
|
|
443
537
|
let filesScanned = 0;
|
|
444
538
|
let skippedInaccessible = 0;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
539
|
+
const perFile = new Map();
|
|
540
|
+
let dispatched = 0;
|
|
541
|
+
let collected = 0;
|
|
542
|
+
const stopped = await processEntriesConcurrently(entries, {
|
|
543
|
+
signal: options.signal,
|
|
544
|
+
concurrency: PARALLEL_CONCURRENCY,
|
|
545
|
+
shouldStop: () => collected >= maxResults,
|
|
546
|
+
onEntry: () => {
|
|
547
|
+
dispatched++;
|
|
548
|
+
},
|
|
549
|
+
onError: () => undefined,
|
|
550
|
+
runEntry: async (entryPath) => {
|
|
551
|
+
const index = dispatched - 1;
|
|
456
552
|
try {
|
|
457
|
-
await pathGuard.validateExistingPath(
|
|
553
|
+
await pathGuard.validateExistingPath(entryPath);
|
|
554
|
+
perFile.set(index, { path: entryPath });
|
|
555
|
+
collected++;
|
|
458
556
|
}
|
|
459
|
-
catch {
|
|
460
|
-
|
|
461
|
-
|
|
557
|
+
catch (error) {
|
|
558
|
+
if (pathGuard.isSensitive(entryPath) ||
|
|
559
|
+
(isFsError(error) && error.message.startsWith('Sensitive file'))) {
|
|
560
|
+
perFile.set(index, 'sensitive');
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
perFile.set(index, 'inaccessible');
|
|
564
|
+
}
|
|
462
565
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
for (let i = 0; i < dispatched; i++) {
|
|
569
|
+
const item = perFile.get(i);
|
|
570
|
+
if (!item || item === 'sensitive')
|
|
571
|
+
continue;
|
|
572
|
+
if (item === 'inaccessible') {
|
|
573
|
+
skippedInaccessible++;
|
|
574
|
+
continue;
|
|
467
575
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
stoppedByAbort = true;
|
|
576
|
+
if (results.length >= maxResults)
|
|
577
|
+
break;
|
|
578
|
+
filesScanned++;
|
|
579
|
+
results.push(item);
|
|
473
580
|
}
|
|
474
581
|
// Sorting — only name / path are supported; size / modified were removed
|
|
475
582
|
// (the glob never collected stats, so they were always undefined).
|
|
@@ -480,7 +587,7 @@ export async function searchFiles(directory, pattern, options, pathGuard) {
|
|
|
480
587
|
results.sort((a, b) => a.path.localeCompare(b.path));
|
|
481
588
|
}
|
|
482
589
|
// Same precedence as above: the result cap wins over a same-iteration abort.
|
|
483
|
-
const stoppedReason = resolveStopReason(results.length >= maxResults,
|
|
590
|
+
const stoppedReason = resolveStopReason(results.length >= maxResults, stopped === 'timeout' || Boolean(options.signal?.aborted));
|
|
484
591
|
return {
|
|
485
592
|
basePath: directory,
|
|
486
593
|
results,
|
|
@@ -493,4 +600,23 @@ export async function searchFiles(directory, pattern, options, pathGuard) {
|
|
|
493
600
|
},
|
|
494
601
|
};
|
|
495
602
|
}
|
|
603
|
+
/** Explicit files bypass glob filtering; their parent is the directory walked. */
|
|
604
|
+
export async function resolveSearchScope(path, fs, signal) {
|
|
605
|
+
const requested = fs.pathGuard.resolvePathOrRoot(path);
|
|
606
|
+
// One resolution, one stat: validateExistingDirectory would redo both.
|
|
607
|
+
const { stats, validPath: resolved } = await fs.stat(requested, { signal });
|
|
608
|
+
if (!stats.isFile()) {
|
|
609
|
+
if (!stats.isDirectory()) {
|
|
610
|
+
throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);
|
|
611
|
+
}
|
|
612
|
+
return { basePath: resolved };
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
basePath: dirname(resolved),
|
|
616
|
+
// The name is NOT turned into a glob: `[slug].tsx` would read as a
|
|
617
|
+
// character class. searchContent scans exactly this file instead, and
|
|
618
|
+
// hidden/ignored filtering is moot for a file the caller named.
|
|
619
|
+
explicitFile: resolved,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
496
622
|
//# sourceMappingURL=search.js.map
|